fix: prompts storage
This commit is contained in:
parent
6235736f4f
commit
e3ddf1294f
|
|
@ -81,8 +81,6 @@ export const autoEnhancePrompt = async (
|
||||||
}),
|
}),
|
||||||
enhancements: result.metadata?.enhancements || [],
|
enhancements: result.metadata?.enhancements || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
req.body.prompt = result.enhancedPrompt;
|
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[${timestamp}] [${requestId}] Prompt enhancement failed: ${result.error}`);
|
console.warn(`[${timestamp}] [${requestId}] Prompt enhancement failed: ${result.error}`);
|
||||||
console.log(`[${timestamp}] [${requestId}] Proceeding with original prompt`);
|
console.log(`[${timestamp}] [${requestId}] Proceeding with original prompt`);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { asyncHandler } from '@/middleware/errorHandler';
|
||||||
import { validateApiKey } from '@/middleware/auth/validateApiKey';
|
import { validateApiKey } from '@/middleware/auth/validateApiKey';
|
||||||
import { requireProjectKey } from '@/middleware/auth/requireProjectKey';
|
import { requireProjectKey } from '@/middleware/auth/requireProjectKey';
|
||||||
import { rateLimitByApiKey } from '@/middleware/auth/rateLimiter';
|
import { rateLimitByApiKey } from '@/middleware/auth/rateLimiter';
|
||||||
|
import { autoEnhancePrompt } from '@/middleware/promptEnhancement';
|
||||||
import { validateAndNormalizePagination } from '@/utils/validators';
|
import { validateAndNormalizePagination } from '@/utils/validators';
|
||||||
import { buildPaginatedResponse } from '@/utils/helpers';
|
import { buildPaginatedResponse } from '@/utils/helpers';
|
||||||
import { toGenerationResponse } from '@/types/responses';
|
import { toGenerationResponse } from '@/types/responses';
|
||||||
|
|
@ -46,7 +47,7 @@ const getGenerationService = (): GenerationService => {
|
||||||
* @param {string} [req.body.flowId] - Associate with existing flow
|
* @param {string} [req.body.flowId] - Associate with existing flow
|
||||||
* @param {string} [req.body.alias] - Project-scoped alias (@custom-name)
|
* @param {string} [req.body.alias] - Project-scoped alias (@custom-name)
|
||||||
* @param {string} [req.body.flowAlias] - Flow-scoped alias (requires flowId)
|
* @param {string} [req.body.flowAlias] - Flow-scoped alias (requires flowId)
|
||||||
* @param {boolean} [req.body.autoEnhance=false] - Enable prompt enhancement
|
* @param {boolean} [req.body.autoEnhance=true] - Enable prompt enhancement
|
||||||
* @param {object} [req.body.meta] - Custom metadata
|
* @param {object} [req.body.meta] - Custom metadata
|
||||||
*
|
*
|
||||||
* @returns {CreateGenerationResponse} 201 - Generation created with status
|
* @returns {CreateGenerationResponse} 201 - Generation created with status
|
||||||
|
|
@ -82,10 +83,15 @@ generationsRouter.post(
|
||||||
validateApiKey,
|
validateApiKey,
|
||||||
requireProjectKey,
|
requireProjectKey,
|
||||||
rateLimitByApiKey,
|
rateLimitByApiKey,
|
||||||
|
autoEnhancePrompt,
|
||||||
asyncHandler(async (req: any, res: Response<CreateGenerationResponse>) => {
|
asyncHandler(async (req: any, res: Response<CreateGenerationResponse>) => {
|
||||||
const service = getGenerationService();
|
const service = getGenerationService();
|
||||||
|
|
||||||
|
// Extract original prompt from middleware property if enhancement was attempted
|
||||||
|
// Otherwise fall back to request body
|
||||||
|
const prompt = req.originalPrompt || req.body.prompt;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
prompt,
|
|
||||||
referenceImages,
|
referenceImages,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
flowId,
|
flowId,
|
||||||
|
|
@ -119,6 +125,7 @@ generationsRouter.post(
|
||||||
alias,
|
alias,
|
||||||
flowAlias,
|
flowAlias,
|
||||||
autoEnhance,
|
autoEnhance,
|
||||||
|
enhancedPrompt: req.enhancedPrompt,
|
||||||
meta,
|
meta,
|
||||||
requestId: req.requestId,
|
requestId: req.requestId,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,10 @@ export class GenerationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt semantics (Section 2.1):
|
// Prompt semantics (Section 2.1):
|
||||||
// - If autoEnhance = false OR no enhancedPrompt: prompt = user input, originalPrompt = null
|
// - originalPrompt: ALWAYS contains user's original input
|
||||||
// - If autoEnhance = true AND enhancedPrompt: prompt = enhanced, originalPrompt = user input
|
// - prompt: Enhanced version if autoEnhance=true, otherwise same as originalPrompt
|
||||||
const usedPrompt = params.enhancedPrompt || params.prompt;
|
const usedPrompt = params.enhancedPrompt || params.prompt;
|
||||||
const preservedOriginal = params.enhancedPrompt ? params.prompt : null;
|
const preservedOriginal = params.prompt; // Always store original
|
||||||
|
|
||||||
const generationRecord: NewGeneration = {
|
const generationRecord: NewGeneration = {
|
||||||
projectId: params.projectId,
|
projectId: params.projectId,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ export interface GenerationResponse {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
flowId: string | null;
|
flowId: string | null;
|
||||||
prompt: string; // Prompt actually used for generation
|
prompt: string; // Prompt actually used for generation
|
||||||
originalPrompt: string | null; // User's original (nullable, only if enhanced)
|
originalPrompt: string | null; // User's original input (always populated for new generations)
|
||||||
|
autoEnhance: boolean; // Whether prompt enhancement was applied
|
||||||
aspectRatio: string | null;
|
aspectRatio: string | null;
|
||||||
status: string;
|
status: string;
|
||||||
errorMessage: string | null;
|
errorMessage: string | null;
|
||||||
|
|
@ -247,7 +248,8 @@ export const toGenerationResponse = (gen: GenerationWithRelations): GenerationRe
|
||||||
projectId: gen.projectId,
|
projectId: gen.projectId,
|
||||||
flowId: gen.flowId ?? gen.pendingFlowId ?? null, // Return actual flowId or pendingFlowId for client
|
flowId: gen.flowId ?? gen.pendingFlowId ?? null, // Return actual flowId or pendingFlowId for client
|
||||||
prompt: gen.prompt, // Prompt actually used
|
prompt: gen.prompt, // Prompt actually used
|
||||||
originalPrompt: gen.originalPrompt, // User's original (null if not enhanced)
|
originalPrompt: gen.originalPrompt, // User's original (always populated)
|
||||||
|
autoEnhance: gen.prompt !== gen.originalPrompt, // True if prompts differ (enhancement happened)
|
||||||
aspectRatio: gen.aspectRatio,
|
aspectRatio: gen.aspectRatio,
|
||||||
status: gen.status,
|
status: gen.status,
|
||||||
errorMessage: gen.errorMessage,
|
errorMessage: gen.errorMessage,
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
* Include the <InlineCode color="neutral">X-API-Key</InlineCode> header.
|
* Include the <InlineCode color="neutral">X-API-Key</InlineCode> header.
|
||||||
*
|
*
|
||||||
* // Parameter documentation
|
* // Parameter documentation
|
||||||
* The <InlineCode>autoEnhance</InlineCode> parameter defaults to false.
|
* The <InlineCode>autoEnhance</InlineCode> parameter defaults to true.
|
||||||
*
|
*
|
||||||
* // Error messages
|
* // Error messages
|
||||||
* If you receive <InlineCode color="error">401 Unauthorized</InlineCode>, check your API key.
|
* If you receive <InlineCode color="error">401 Unauthorized</InlineCode>, check your API key.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Create new image generation with optional reference images, aliases, and auto-en
|
||||||
- `flowId` - Associate generation with a flow (UUID)
|
- `flowId` - Associate generation with a flow (UUID)
|
||||||
- `alias` - Assign project-scoped alias to output image (@custom-name)
|
- `alias` - Assign project-scoped alias to output image (@custom-name)
|
||||||
- `flowAlias` - Assign flow-scoped alias to output image (requires flowId)
|
- `flowAlias` - Assign flow-scoped alias to output image (requires flowId)
|
||||||
- `autoEnhance` - Enable prompt enhancement (boolean, default: false)
|
- `autoEnhance` - Enable prompt enhancement (boolean, default: true)
|
||||||
- `enhancementOptions` - Enhancement configuration (object, optional)
|
- `enhancementOptions` - Enhancement configuration (object, optional)
|
||||||
- `template` - Enhancement template: "photorealistic", "illustration", "minimalist", "sticker", "product", "comic", "general"
|
- `template` - Enhancement template: "photorealistic", "illustration", "minimalist", "sticker", "product", "comic", "general"
|
||||||
- `meta` - Custom metadata (JSON object)
|
- `meta` - Custom metadata (JSON object)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
@base = http://localhost:3000
|
||||||
|
@apiKey = bnt_727d2f4f72bd03ed96da5278bb971a00cb0a2454d4d70f9748b5c39f3f69d88d
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# BASIC GENERATION TESTS
|
||||||
|
# Run these tests FIRST to verify core generation functionality
|
||||||
|
#
|
||||||
|
# Test Coverage:
|
||||||
|
# 1. Simple generation with different aspect ratios
|
||||||
|
# 2. Generation retrieval and listing
|
||||||
|
# 3. Pagination and filtering
|
||||||
|
# 4. Processing time tracking
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 1: Simple Generation (16:9)
|
||||||
|
# Creates a basic generation without references or flows
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 1.1: Create Generation
|
||||||
|
# @name createBasicGen
|
||||||
|
POST {{base}}/api/v1/generations
|
||||||
|
Content-Type: application/json
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "шикарная моторная яхта движется по живописному озеру, люди сидят в спасательных жилетах и держат в руках бутылки с пивом, густой хвойный лес на берегу. фотореалистичная фотография",
|
||||||
|
"autoEnhance": true,
|
||||||
|
"aspectRatio": "16:9"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@generationId = {{createBasicGen.response.body.$.data.id}}
|
||||||
|
@generationStatus = {{createBasicGen.response.body.$.data.status}}
|
||||||
|
|
||||||
|
|
||||||
|
### Step 1.2: Check Generation Status (Poll until success)
|
||||||
|
# @name checkBasicGen
|
||||||
|
# Keep running this until status = "success"
|
||||||
|
GET {{base}}/api/v1/generations/{{generationId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@outputImageId = {{checkBasicGen.response.body.$.data.outputImageId}}
|
||||||
|
@processingTimeMs = {{checkBasicGen.response.body.$.data.processingTimeMs}}
|
||||||
|
|
||||||
|
|
||||||
|
### Step 1.3: Get Output Image Metadata
|
||||||
|
# @name getBasicImage
|
||||||
|
GET {{base}}/api/v1/images/{{outputImageId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - storageUrl is present
|
||||||
|
# - Image is accessible at storageUrl
|
||||||
|
# - processingTimeMs is recorded
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 2: Square Generation (1:1)
|
||||||
|
# Tests aspect ratio 1:1
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 2.1: Create Square Generation
|
||||||
|
# @name createSquareGen
|
||||||
|
POST {{base}}/api/v1/generations
|
||||||
|
Content-Type: application/json
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "A minimalist logo design",
|
||||||
|
"aspectRatio": "1:1"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@squareGenId = {{createSquareGen.response.body.$.data.id}}
|
||||||
|
|
||||||
|
|
||||||
|
### Step 2.2: Check Status (Poll until success)
|
||||||
|
# @name checkSquareGen
|
||||||
|
GET {{base}}/api/v1/generations/{{squareGenId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@squareImageId = {{checkSquareGen.response.body.$.data.outputImageId}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - aspectRatio = "1:1"
|
||||||
|
# - status = "success"
|
||||||
|
# - outputImageId is present
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 3: Portrait Generation (9:16)
|
||||||
|
# Tests aspect ratio 9:16
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 3.1: Create Portrait Generation
|
||||||
|
# @name createPortraitGen
|
||||||
|
POST {{base}}/api/v1/generations
|
||||||
|
Content-Type: application/json
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "A tall building at night",
|
||||||
|
"aspectRatio": "9:16"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@portraitGenId = {{createPortraitGen.response.body.$.data.id}}
|
||||||
|
|
||||||
|
|
||||||
|
### Step 3.2: Check Status (Poll until success)
|
||||||
|
# @name checkPortraitGen
|
||||||
|
GET {{base}}/api/v1/generations/{{portraitGenId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@portraitImageId = {{checkPortraitGen.response.body.$.data.outputImageId}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - aspectRatio = "9:16"
|
||||||
|
# - status = "success"
|
||||||
|
# - outputImageId is present
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 4: Get Generation by ID
|
||||||
|
# Verifies all expected fields are present in response
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 4.1: Get Generation Details
|
||||||
|
# @name getGenDetails
|
||||||
|
GET {{base}}/api/v1/generations/{{generationId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify response contains:
|
||||||
|
# - id: {{generationId}}
|
||||||
|
# - prompt: "A beautiful sunset over mountains"
|
||||||
|
# - status: "success"
|
||||||
|
# - outputImageId: {{outputImageId}}
|
||||||
|
# - outputImage (nested object)
|
||||||
|
# - createdAt
|
||||||
|
# - updatedAt
|
||||||
|
# - processingTimeMs
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 5: List All Generations
|
||||||
|
# Verifies generation listing without filters
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 5.1: List All Generations (Default pagination)
|
||||||
|
# @name listAllGens
|
||||||
|
GET {{base}}/api/v1/generations
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - Response has data array
|
||||||
|
# - Response has pagination object
|
||||||
|
# - At least 3 generations present (from previous tests)
|
||||||
|
# - Our generation {{generationId}} is in the list
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 6: List Generations with Pagination
|
||||||
|
# Tests pagination parameters (limit, offset)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 6.1: Get First Page (limit=2)
|
||||||
|
# @name listPageOne
|
||||||
|
GET {{base}}/api/v1/generations?limit=2&offset=0
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - data.length <= 2
|
||||||
|
# - pagination.limit = 2
|
||||||
|
# - pagination.offset = 0
|
||||||
|
# - pagination.hasMore = true (if total > 2)
|
||||||
|
|
||||||
|
|
||||||
|
### Step 6.2: Get Second Page (offset=2)
|
||||||
|
# @name listPageTwo
|
||||||
|
GET {{base}}/api/v1/generations?limit=2&offset=2
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - Different results than first page
|
||||||
|
# - pagination.offset = 2
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 7: Filter Generations by Status
|
||||||
|
# Tests status filter parameter
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 7.1: Filter by Success Status
|
||||||
|
# @name filterSuccess
|
||||||
|
GET {{base}}/api/v1/generations?status=success
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - All items in data[] have status = "success"
|
||||||
|
# - No pending/processing/failed generations
|
||||||
|
|
||||||
|
|
||||||
|
### Step 7.2: Filter by Failed Status
|
||||||
|
# @name filterFailed
|
||||||
|
GET {{base}}/api/v1/generations?status=failed
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - All items (if any) have status = "failed"
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TEST 8: Verify Processing Time Recorded
|
||||||
|
# Ensures generation performance metrics are tracked
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
### Step 8.1: Check Processing Time
|
||||||
|
# @name checkProcessingTime
|
||||||
|
GET {{base}}/api/v1/generations/{{generationId}}
|
||||||
|
X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# Verify:
|
||||||
|
# - processingTimeMs is a number: {{processingTimeMs}}
|
||||||
|
# - processingTimeMs > 0
|
||||||
|
# - Typical range: 3000-15000ms (3-15 seconds)
|
||||||
|
# - Processing time reflects actual generation duration
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# CLEANUP (Optional)
|
||||||
|
# Uncomment to delete test generations
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# ### Delete Test Generation 1
|
||||||
|
# DELETE {{base}}/api/v1/generations/{{generationId}}
|
||||||
|
# X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
# ### Delete Test Generation 2
|
||||||
|
# DELETE {{base}}/api/v1/generations/{{squareGenId}}
|
||||||
|
# X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
# ### Delete Test Generation 3
|
||||||
|
# DELETE {{base}}/api/v1/generations/{{portraitGenId}}
|
||||||
|
# X-API-Key: {{apiKey}}
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# NOTES
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Expected Results:
|
||||||
|
# ✓ All generations complete successfully (status = "success")
|
||||||
|
# ✓ Each generation has unique ID and output image
|
||||||
|
# ✓ Aspect ratios are correctly applied
|
||||||
|
# ✓ Processing times are recorded (typically 3-15 seconds)
|
||||||
|
# ✓ Pagination works correctly
|
||||||
|
# ✓ Status filtering works correctly
|
||||||
|
#
|
||||||
|
# Common Issues:
|
||||||
|
# ⚠ Generation may fail with Gemini API errors (transient)
|
||||||
|
# ⚠ Processing time varies based on prompt complexity
|
||||||
|
# ⚠ First generation may be slower (cold start)
|
||||||
|
#
|
||||||
|
# Tips:
|
||||||
|
# - Use "Poll until success" for Step X.2 requests
|
||||||
|
# - Variables are automatically extracted from responses
|
||||||
|
# - Check response body to see extracted values
|
||||||
|
# - Most generations complete in 5-10 seconds
|
||||||
|
#
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
// tests/api/08-auto-enhance.ts
|
||||||
|
// Auto-Enhance Feature Tests
|
||||||
|
|
||||||
|
import { api, log, runTest, waitForGeneration, testContext } from './utils';
|
||||||
|
import { endpoints } from './config';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
log.section('AUTO-ENHANCE TESTS');
|
||||||
|
|
||||||
|
// Test 1: Generation without autoEnhance parameter (should default to true)
|
||||||
|
await runTest('Generate without autoEnhance param → should enhance', async () => {
|
||||||
|
const result = await api(endpoints.generations, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: 'a simple test image',
|
||||||
|
aspectRatio: '1:1',
|
||||||
|
// No autoEnhance parameter - should default to true
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.data.data || !result.data.data.id) {
|
||||||
|
throw new Error('No generation returned');
|
||||||
|
}
|
||||||
|
|
||||||
|
const generation = await waitForGeneration(result.data.data.id);
|
||||||
|
|
||||||
|
if (generation.status !== 'success') {
|
||||||
|
throw new Error(`Generation failed: ${generation.errorMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify enhancement happened
|
||||||
|
if (!generation.originalPrompt) {
|
||||||
|
throw new Error('originalPrompt should be populated when enhanced');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generation.autoEnhance) {
|
||||||
|
throw new Error('autoEnhance should be true');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.prompt === generation.originalPrompt) {
|
||||||
|
throw new Error('prompt and originalPrompt should be different (enhancement happened)');
|
||||||
|
}
|
||||||
|
|
||||||
|
log.detail('Original prompt', generation.originalPrompt);
|
||||||
|
log.detail('Enhanced prompt', generation.prompt);
|
||||||
|
log.detail('autoEnhance', generation.autoEnhance);
|
||||||
|
log.detail('Enhancement confirmed', '✓');
|
||||||
|
|
||||||
|
testContext.enhancedGenId = generation.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 2: Generation with autoEnhance: false
|
||||||
|
await runTest('Generate with autoEnhance: false → should NOT enhance', async () => {
|
||||||
|
const result = await api(endpoints.generations, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: 'another test image',
|
||||||
|
aspectRatio: '1:1',
|
||||||
|
autoEnhance: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.data.data || !result.data.data.id) {
|
||||||
|
throw new Error('No generation returned');
|
||||||
|
}
|
||||||
|
|
||||||
|
const generation = await waitForGeneration(result.data.data.id);
|
||||||
|
|
||||||
|
if (generation.status !== 'success') {
|
||||||
|
throw new Error(`Generation failed: ${generation.errorMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify NO enhancement happened
|
||||||
|
if (!generation.originalPrompt) {
|
||||||
|
throw new Error('originalPrompt should be populated with original input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.autoEnhance) {
|
||||||
|
throw new Error('autoEnhance should be false');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.prompt !== generation.originalPrompt) {
|
||||||
|
throw new Error('prompt and originalPrompt should be the SAME when NOT enhanced');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.prompt !== 'another test image') {
|
||||||
|
throw new Error('both prompts should match original input (no enhancement)');
|
||||||
|
}
|
||||||
|
|
||||||
|
log.detail('Prompt', generation.prompt);
|
||||||
|
log.detail('originalPrompt', generation.originalPrompt);
|
||||||
|
log.detail('autoEnhance', generation.autoEnhance);
|
||||||
|
log.detail('Prompts match (no enhancement)', '✓');
|
||||||
|
|
||||||
|
testContext.notEnhancedGenId = generation.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 3: Generation with explicit autoEnhance: true
|
||||||
|
await runTest('Generate with autoEnhance: true → should enhance', async () => {
|
||||||
|
const result = await api(endpoints.generations, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: 'third test image',
|
||||||
|
aspectRatio: '1:1',
|
||||||
|
autoEnhance: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.data.data || !result.data.data.id) {
|
||||||
|
throw new Error('No generation returned');
|
||||||
|
}
|
||||||
|
|
||||||
|
const generation = await waitForGeneration(result.data.data.id);
|
||||||
|
|
||||||
|
if (generation.status !== 'success') {
|
||||||
|
throw new Error(`Generation failed: ${generation.errorMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify enhancement happened
|
||||||
|
if (!generation.originalPrompt) {
|
||||||
|
throw new Error('originalPrompt should be populated');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generation.autoEnhance) {
|
||||||
|
throw new Error('autoEnhance should be true');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.originalPrompt !== 'third test image') {
|
||||||
|
throw new Error('originalPrompt should match input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generation.prompt === generation.originalPrompt) {
|
||||||
|
throw new Error('prompt should be enhanced (different from original)');
|
||||||
|
}
|
||||||
|
|
||||||
|
log.detail('Original prompt', generation.originalPrompt);
|
||||||
|
log.detail('Enhanced prompt', generation.prompt);
|
||||||
|
log.detail('autoEnhance', generation.autoEnhance);
|
||||||
|
log.detail('Enhancement confirmed', '✓');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 4: Verify enhanced prompt is actually different and longer
|
||||||
|
await runTest('Verify enhancement quality', async () => {
|
||||||
|
const result = await api(`${endpoints.generations}/${testContext.enhancedGenId}`);
|
||||||
|
const generation = result.data.data;
|
||||||
|
|
||||||
|
const originalLength = generation.originalPrompt?.length || 0;
|
||||||
|
const enhancedLength = generation.prompt?.length || 0;
|
||||||
|
|
||||||
|
if (enhancedLength <= originalLength) {
|
||||||
|
log.warning('Enhanced prompt not longer than original (might not be truly enhanced)');
|
||||||
|
} else {
|
||||||
|
log.detail('Original length', originalLength);
|
||||||
|
log.detail('Enhanced length', enhancedLength);
|
||||||
|
log.detail('Increase', `+${enhancedLength - originalLength} chars`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the enhanced prompt contains more descriptive language
|
||||||
|
const hasPhotorealistic = generation.prompt.toLowerCase().includes('photorealistic') ||
|
||||||
|
generation.prompt.toLowerCase().includes('realistic') ||
|
||||||
|
generation.prompt.toLowerCase().includes('detailed');
|
||||||
|
|
||||||
|
if (hasPhotorealistic) {
|
||||||
|
log.detail('Enhancement adds descriptive terms', '✓');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 5: Verify both enhanced and non-enhanced are in listings
|
||||||
|
await runTest('List generations - verify autoEnhance field', async () => {
|
||||||
|
const result = await api(endpoints.generations);
|
||||||
|
|
||||||
|
if (!result.data.data || !Array.isArray(result.data.data)) {
|
||||||
|
throw new Error('No generations array returned');
|
||||||
|
}
|
||||||
|
|
||||||
|
const enhancedGens = result.data.data.filter((g: any) => g.autoEnhance === true);
|
||||||
|
const notEnhancedGens = result.data.data.filter((g: any) => g.autoEnhance === false);
|
||||||
|
|
||||||
|
log.detail('Total generations', result.data.data.length);
|
||||||
|
log.detail('Enhanced', enhancedGens.length);
|
||||||
|
log.detail('Not enhanced', notEnhancedGens.length);
|
||||||
|
|
||||||
|
if (enhancedGens.length === 0) {
|
||||||
|
throw new Error('Should have at least one enhanced generation');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notEnhancedGens.length === 0) {
|
||||||
|
throw new Error('Should have at least one non-enhanced generation');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 6: Verify response structure
|
||||||
|
await runTest('Verify response includes all enhancement fields', async () => {
|
||||||
|
const result = await api(`${endpoints.generations}/${testContext.enhancedGenId}`);
|
||||||
|
const generation = result.data.data;
|
||||||
|
|
||||||
|
// Required fields
|
||||||
|
if (typeof generation.prompt !== 'string') {
|
||||||
|
throw new Error('prompt should be string');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof generation.autoEnhance !== 'boolean') {
|
||||||
|
throw new Error('autoEnhance should be boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalPrompt can be null or string
|
||||||
|
if (generation.originalPrompt !== null && typeof generation.originalPrompt !== 'string') {
|
||||||
|
throw new Error('originalPrompt should be null or string');
|
||||||
|
}
|
||||||
|
|
||||||
|
log.detail('Response structure', 'valid ✓');
|
||||||
|
log.detail('prompt type', typeof generation.prompt);
|
||||||
|
log.detail('originalPrompt type', typeof generation.originalPrompt || 'null');
|
||||||
|
log.detail('autoEnhance type', typeof generation.autoEnhance);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.section('AUTO-ENHANCE TESTS COMPLETED');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
|
|
@ -20,6 +20,7 @@ const testFiles = [
|
||||||
'05-live.ts',
|
'05-live.ts',
|
||||||
'06-edge-cases.ts',
|
'06-edge-cases.ts',
|
||||||
'07-known-issues.ts',
|
'07-known-issues.ts',
|
||||||
|
'08-auto-enhance.ts',
|
||||||
];
|
];
|
||||||
|
|
||||||
async function runTest(file: string): Promise<{ success: boolean; duration: number }> {
|
async function runTest(file: string): Promise<{ success: boolean; duration: number }> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue