Commit Β·
9de719c
1
Parent(s): addcf34
Refactor extensive ad generation to support batch responses
Browse files- Updated the API endpoint to return all generated ads instead of just the first one, enhancing the batch generation feature.
- Modified the frontend components to handle and display batch results, including updates to the GeneratePage and ExtensiveForm for better user feedback.
- Changed variable names and comments for clarity, ensuring consistency in terminology related to ad images and variations.
- Improved error handling and logging in the generator service to accommodate the new batch processing logic.
- frontend/app/generate/matrix/page.tsx +1 -1
- frontend/app/generate/page.tsx +11 -10
- frontend/components/generation/AdPreview.tsx +3 -26
- frontend/components/generation/ExtensiveForm.tsx +13 -1
- frontend/components/generation/GenerationForm.tsx +1 -1
- frontend/lib/api/endpoints.ts +2 -2
- main.py +8 -4
- services/generator.py +3 -4
frontend/app/generate/matrix/page.tsx
CHANGED
|
@@ -170,7 +170,7 @@ export default function MatrixGeneratePage() {
|
|
| 170 |
|
| 171 |
<div>
|
| 172 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 173 |
-
Number of Images: <span className="text-cyan-600 font-bold">{numImages}</span>
|
| 174 |
</label>
|
| 175 |
<input
|
| 176 |
type="range"
|
|
|
|
| 170 |
|
| 171 |
<div>
|
| 172 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 173 |
+
Number of Ad Images: <span className="text-cyan-600 font-bold">{numImages}</span>
|
| 174 |
</label>
|
| 175 |
<input
|
| 176 |
type="range"
|
frontend/app/generate/page.tsx
CHANGED
|
@@ -491,7 +491,7 @@ export default function GeneratePage() {
|
|
| 491 |
message: progressSteps[0].message,
|
| 492 |
});
|
| 493 |
|
| 494 |
-
// Generate ad using extensive
|
| 495 |
const result = await generateExtensiveAd(data);
|
| 496 |
|
| 497 |
// Clear progress interval
|
|
@@ -500,12 +500,9 @@ export default function GeneratePage() {
|
|
| 500 |
progressInterval = null;
|
| 501 |
}
|
| 502 |
|
| 503 |
-
//
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
console.log(`π
Generated at: ${result.created_at || 'N/A'}`);
|
| 507 |
-
console.log(`π Note: Generate page shows images from the first strategy only. All strategies are saved separately in the database and appear on the Dashboard.`);
|
| 508 |
-
}
|
| 509 |
|
| 510 |
setProgress({
|
| 511 |
step: "saving",
|
|
@@ -516,14 +513,18 @@ export default function GeneratePage() {
|
|
| 516 |
// Small delay to show saving step
|
| 517 |
await new Promise(resolve => setTimeout(resolve, 500));
|
| 518 |
|
| 519 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
setProgress({
|
| 521 |
step: "complete",
|
| 522 |
progress: 100,
|
| 523 |
message: "Ad generated successfully!",
|
| 524 |
});
|
| 525 |
|
| 526 |
-
toast.success(
|
| 527 |
} catch (error: any) {
|
| 528 |
// Clear progress interval on error
|
| 529 |
if (progressInterval) {
|
|
@@ -701,7 +702,7 @@ export default function GeneratePage() {
|
|
| 701 |
|
| 702 |
<div>
|
| 703 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 704 |
-
Number of Images: <span className="text-blue-600 font-bold">{numImages}</span>
|
| 705 |
</label>
|
| 706 |
<input
|
| 707 |
type="range"
|
|
|
|
| 491 |
message: progressSteps[0].message,
|
| 492 |
});
|
| 493 |
|
| 494 |
+
// Generate ad using extensive - returns BatchResponse like batch flow
|
| 495 |
const result = await generateExtensiveAd(data);
|
| 496 |
|
| 497 |
// Clear progress interval
|
|
|
|
| 500 |
progressInterval = null;
|
| 501 |
}
|
| 502 |
|
| 503 |
+
// Store results like batch generation
|
| 504 |
+
setBatchResults(result.ads);
|
| 505 |
+
setCurrentBatchIndex(0);
|
|
|
|
|
|
|
|
|
|
| 506 |
|
| 507 |
setProgress({
|
| 508 |
step: "saving",
|
|
|
|
| 513 |
// Small delay to show saving step
|
| 514 |
await new Promise(resolve => setTimeout(resolve, 500));
|
| 515 |
|
| 516 |
+
// Show first result
|
| 517 |
+
if (result.ads.length > 0) {
|
| 518 |
+
setCurrentGeneration(result.ads[0]);
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
setProgress({
|
| 522 |
step: "complete",
|
| 523 |
progress: 100,
|
| 524 |
message: "Ad generated successfully!",
|
| 525 |
});
|
| 526 |
|
| 527 |
+
toast.success(`Generated ${result.count} ad(s) successfully using Extensive!`);
|
| 528 |
} catch (error: any) {
|
| 529 |
// Clear progress interval on error
|
| 530 |
if (progressInterval) {
|
|
|
|
| 702 |
|
| 703 |
<div>
|
| 704 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 705 |
+
Number of Ad Images: <span className="text-blue-600 font-bold">{numImages}</span>
|
| 706 |
</label>
|
| 707 |
<input
|
| 708 |
type="range"
|
frontend/components/generation/AdPreview.tsx
CHANGED
|
@@ -238,29 +238,6 @@ export const AdPreview: React.FC<AdPreviewProps> = ({ ad }) => {
|
|
| 238 |
</div>
|
| 239 |
)}
|
| 240 |
|
| 241 |
-
{/* Headline */}
|
| 242 |
-
<div className="bg-white rounded-2xl shadow-lg shadow-blue-100/30 p-6 border-l-4 border-blue-500">
|
| 243 |
-
<div className="flex items-start justify-between gap-4 mb-3">
|
| 244 |
-
<h3 className="text-xs font-bold text-blue-600 uppercase tracking-wider">Headline</h3>
|
| 245 |
-
<div className="relative group">
|
| 246 |
-
<Button
|
| 247 |
-
variant="ghost"
|
| 248 |
-
size="sm"
|
| 249 |
-
onClick={() => handleCopyText(ad.headline, "Headline")}
|
| 250 |
-
className="text-blue-500 hover:bg-blue-50"
|
| 251 |
-
>
|
| 252 |
-
<Copy className="h-4 w-4" />
|
| 253 |
-
</Button>
|
| 254 |
-
<span className="absolute -bottom-8 left-1/2 -translate-x-1/2 px-2 py-1 bg-blue-600 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-10">
|
| 255 |
-
Copy Headline
|
| 256 |
-
</span>
|
| 257 |
-
</div>
|
| 258 |
-
</div>
|
| 259 |
-
<h1 className="text-2xl font-bold bg-gradient-to-r from-gray-900 to-gray-700 bg-clip-text text-transparent leading-tight">
|
| 260 |
-
{ad.headline}
|
| 261 |
-
</h1>
|
| 262 |
-
</div>
|
| 263 |
-
|
| 264 |
{/* Description */}
|
| 265 |
{ad.description && (
|
| 266 |
<div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-violet-500">
|
|
@@ -283,10 +260,7 @@ export const AdPreview: React.FC<AdPreviewProps> = ({ ad }) => {
|
|
| 283 |
<p className="text-gray-700 leading-relaxed">{ad.description}</p>
|
| 284 |
</div>
|
| 285 |
)}
|
| 286 |
-
</div>
|
| 287 |
|
| 288 |
-
{/* Right Column - Additional Info */}
|
| 289 |
-
<div className="space-y-5">
|
| 290 |
{/* Body Story */}
|
| 291 |
{ad.body_story && (
|
| 292 |
<div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-amber-500">
|
|
@@ -337,7 +311,10 @@ export const AdPreview: React.FC<AdPreviewProps> = ({ ad }) => {
|
|
| 337 |
</div>
|
| 338 |
</div>
|
| 339 |
)}
|
|
|
|
| 340 |
|
|
|
|
|
|
|
| 341 |
{/* CTA */}
|
| 342 |
{ad.cta && (
|
| 343 |
<div className="bg-gradient-to-r from-emerald-50 to-teal-50 rounded-2xl shadow-md p-6 border border-emerald-200">
|
|
|
|
| 238 |
</div>
|
| 239 |
)}
|
| 240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
{/* Description */}
|
| 242 |
{ad.description && (
|
| 243 |
<div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-violet-500">
|
|
|
|
| 260 |
<p className="text-gray-700 leading-relaxed">{ad.description}</p>
|
| 261 |
</div>
|
| 262 |
)}
|
|
|
|
| 263 |
|
|
|
|
|
|
|
| 264 |
{/* Body Story */}
|
| 265 |
{ad.body_story && (
|
| 266 |
<div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-amber-500">
|
|
|
|
| 311 |
</div>
|
| 312 |
</div>
|
| 313 |
)}
|
| 314 |
+
</div>
|
| 315 |
|
| 316 |
+
{/* Right Column - CTA and Strategy */}
|
| 317 |
+
<div className="space-y-5">
|
| 318 |
{/* CTA */}
|
| 319 |
{ad.cta && (
|
| 320 |
<div className="bg-gradient-to-r from-emerald-50 to-teal-50 rounded-2xl shadow-md p-6 border border-emerald-200">
|
frontend/components/generation/ExtensiveForm.tsx
CHANGED
|
@@ -115,7 +115,7 @@ export const ExtensiveForm: React.FC<ExtensiveFormProps> = ({
|
|
| 115 |
|
| 116 |
<div>
|
| 117 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 118 |
-
|
| 119 |
</label>
|
| 120 |
<input
|
| 121 |
type="range"
|
|
@@ -129,6 +129,9 @@ export const ExtensiveForm: React.FC<ExtensiveFormProps> = ({
|
|
| 129 |
<span>1</span>
|
| 130 |
<span>3</span>
|
| 131 |
</div>
|
|
|
|
|
|
|
|
|
|
| 132 |
</div>
|
| 133 |
|
| 134 |
<div>
|
|
@@ -152,6 +155,15 @@ export const ExtensiveForm: React.FC<ExtensiveFormProps> = ({
|
|
| 152 |
</p>
|
| 153 |
</div>
|
| 154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
<button
|
| 156 |
type="submit"
|
| 157 |
disabled={isLoading}
|
|
|
|
| 115 |
|
| 116 |
<div>
|
| 117 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 118 |
+
Variations per Strategy: <span className="text-blue-600 font-bold">{numImages}</span>
|
| 119 |
</label>
|
| 120 |
<input
|
| 121 |
type="range"
|
|
|
|
| 129 |
<span>1</span>
|
| 130 |
<span>3</span>
|
| 131 |
</div>
|
| 132 |
+
<p className="text-xs text-gray-500 mt-1">
|
| 133 |
+
Each variation will have a unique image and slight copy variations
|
| 134 |
+
</p>
|
| 135 |
</div>
|
| 136 |
|
| 137 |
<div>
|
|
|
|
| 155 |
</p>
|
| 156 |
</div>
|
| 157 |
|
| 158 |
+
<div className="bg-gradient-to-r from-blue-50 to-cyan-50 border border-blue-200 rounded-xl p-4">
|
| 159 |
+
<p className="text-sm font-semibold text-gray-800">
|
| 160 |
+
<strong>Estimated:</strong> {numStrategies} ads Γ {numImages} variation(s) = {numStrategies * numImages} total variations
|
| 161 |
+
</p>
|
| 162 |
+
<p className="text-xs text-gray-600 mt-1">
|
| 163 |
+
This may take several minutes to complete
|
| 164 |
+
</p>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
<button
|
| 168 |
type="submit"
|
| 169 |
disabled={isLoading}
|
frontend/components/generation/GenerationForm.tsx
CHANGED
|
@@ -97,7 +97,7 @@ export const GenerationForm: React.FC<GenerationFormProps> = ({
|
|
| 97 |
|
| 98 |
<div>
|
| 99 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 100 |
-
Number of Images: <span className="text-blue-600 font-bold">{numImages}</span>
|
| 101 |
</label>
|
| 102 |
<input
|
| 103 |
type="range"
|
|
|
|
| 97 |
|
| 98 |
<div>
|
| 99 |
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
| 100 |
+
Number of Ad Images: <span className="text-blue-600 font-bold">{numImages}</span>
|
| 101 |
</label>
|
| 102 |
<input
|
| 103 |
type="range"
|
frontend/lib/api/endpoints.ts
CHANGED
|
@@ -77,7 +77,7 @@ export const generateExtensiveAd = async (params: {
|
|
| 77 |
num_images: number;
|
| 78 |
image_model?: string | null;
|
| 79 |
num_strategies: number;
|
| 80 |
-
}): Promise<
|
| 81 |
// Ensure required parameters are always sent
|
| 82 |
const requestParams = {
|
| 83 |
niche: params.niche,
|
|
@@ -87,7 +87,7 @@ export const generateExtensiveAd = async (params: {
|
|
| 87 |
...(params.offer && { offer: params.offer }),
|
| 88 |
...(params.image_model && { image_model: params.image_model }),
|
| 89 |
};
|
| 90 |
-
const response = await apiClient.post<
|
| 91 |
return response.data;
|
| 92 |
};
|
| 93 |
|
|
|
|
| 77 |
num_images: number;
|
| 78 |
image_model?: string | null;
|
| 79 |
num_strategies: number;
|
| 80 |
+
}): Promise<BatchResponse> => {
|
| 81 |
// Ensure required parameters are always sent
|
| 82 |
const requestParams = {
|
| 83 |
niche: params.niche,
|
|
|
|
| 87 |
...(params.offer && { offer: params.offer }),
|
| 88 |
...(params.image_model && { image_model: params.image_model }),
|
| 89 |
};
|
| 90 |
+
const response = await apiClient.post<BatchResponse>("/extensive/generate", requestParams);
|
| 91 |
return response.data;
|
| 92 |
};
|
| 93 |
|
main.py
CHANGED
|
@@ -1133,7 +1133,7 @@ class ExtensiveGenerateRequest(BaseModel):
|
|
| 1133 |
)
|
| 1134 |
|
| 1135 |
|
| 1136 |
-
@app.post("/extensive/generate", response_model=
|
| 1137 |
async def generate_extensive(
|
| 1138 |
request: ExtensiveGenerateRequest,
|
| 1139 |
username: str = Depends(get_current_user)
|
|
@@ -1150,10 +1150,10 @@ async def generate_extensive(
|
|
| 1150 |
4. Generates image prompts and ad copy in parallel
|
| 1151 |
5. Generates images for each strategy
|
| 1152 |
|
| 1153 |
-
Returns
|
| 1154 |
"""
|
| 1155 |
try:
|
| 1156 |
-
|
| 1157 |
niche=request.niche,
|
| 1158 |
target_audience=request.target_audience,
|
| 1159 |
offer=request.offer,
|
|
@@ -1162,7 +1162,11 @@ async def generate_extensive(
|
|
| 1162 |
num_strategies=request.num_strategies,
|
| 1163 |
username=username, # Pass current user
|
| 1164 |
)
|
| 1165 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1166 |
except Exception as e:
|
| 1167 |
raise HTTPException(status_code=500, detail=str(e))
|
| 1168 |
|
|
|
|
| 1133 |
)
|
| 1134 |
|
| 1135 |
|
| 1136 |
+
@app.post("/extensive/generate", response_model=BatchResponse)
|
| 1137 |
async def generate_extensive(
|
| 1138 |
request: ExtensiveGenerateRequest,
|
| 1139 |
username: str = Depends(get_current_user)
|
|
|
|
| 1150 |
4. Generates image prompts and ad copy in parallel
|
| 1151 |
5. Generates images for each strategy
|
| 1152 |
|
| 1153 |
+
Returns all generated ads from all strategies (like batch generation).
|
| 1154 |
"""
|
| 1155 |
try:
|
| 1156 |
+
results = await ad_generator.generate_ad_extensive(
|
| 1157 |
niche=request.niche,
|
| 1158 |
target_audience=request.target_audience,
|
| 1159 |
offer=request.offer,
|
|
|
|
| 1162 |
num_strategies=request.num_strategies,
|
| 1163 |
username=username, # Pass current user
|
| 1164 |
)
|
| 1165 |
+
# Return as BatchResponse format
|
| 1166 |
+
return BatchResponse(
|
| 1167 |
+
count=len(results),
|
| 1168 |
+
ads=results
|
| 1169 |
+
)
|
| 1170 |
except Exception as e:
|
| 1171 |
raise HTTPException(status_code=500, detail=str(e))
|
| 1172 |
|
services/generator.py
CHANGED
|
@@ -2152,11 +2152,10 @@ CONCEPT: {concept['name']}
|
|
| 2152 |
})
|
| 2153 |
print(f"β Strategy {idx + 1} completed with {len(generated_images)} image(s)")
|
| 2154 |
|
| 2155 |
-
# Return
|
| 2156 |
if all_results:
|
| 2157 |
-
|
| 2158 |
-
|
| 2159 |
-
return result # Return first strategy result with all its images
|
| 2160 |
else:
|
| 2161 |
raise ValueError("No ads generated from extensive")
|
| 2162 |
|
|
|
|
| 2152 |
})
|
| 2153 |
print(f"β Strategy {idx + 1} completed with {len(generated_images)} image(s)")
|
| 2154 |
|
| 2155 |
+
# Return all results (like batch generation)
|
| 2156 |
if all_results:
|
| 2157 |
+
print(f"π€ Returning {len(all_results)} strategy result(s)")
|
| 2158 |
+
return all_results
|
|
|
|
| 2159 |
else:
|
| 2160 |
raise ValueError("No ads generated from extensive")
|
| 2161 |
|