sushilideaclan01 commited on
Commit
b8b7791
·
1 Parent(s): 9474b94

Rename project from Ad Generator Lite to PsyAdGenesis, updating all relevant references and configurations. Enhance descriptions and metadata to reflect the new branding and focus on ad design. Implement sorting functionality in the gallery for improved ad management.

Browse files
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Creative Breakthrough
3
  emoji: 🚀
4
  colorFrom: blue
5
  colorTo: purple
@@ -9,7 +9,9 @@ pinned: false
9
  ---
10
 
11
 
12
- # Ad Generator Lite
 
 
13
 
14
  Generate high-converting ad creatives for Home Insurance and GLP-1 niches using psychological triggers and AI-powered image generation.
15
 
@@ -91,12 +93,12 @@ This app is configured for deployment on Hugging Face Spaces using Docker.
91
  - Go to https://huggingface.co/spaces
92
  - Click "Create new Space"
93
  - Choose "Docker" as the SDK
94
- - Name your space (e.g., `your-username/ad-generator-lite`)
95
 
96
  2. **Push your code** to the Space:
97
  ```bash
98
- git clone https://huggingface.co/spaces/your-username/ad-generator-lite
99
- cd ad-generator-lite
100
  # Copy your files here
101
  git add .
102
  git commit -m "Initial commit"
@@ -140,7 +142,7 @@ LOCAL_IMAGE_RETENTION_HOURS=24 # Images older than this will be auto-deleted
140
 
141
  Once deployed, your app will be available at:
142
  ```
143
- https://your-username-ad-generator-lite.hf.space
144
  ```
145
 
146
  ## API Endpoints
 
1
  ---
2
+ title: PsyAdGenesis
3
  emoji: 🚀
4
  colorFrom: blue
5
  colorTo: purple
 
9
  ---
10
 
11
 
12
+ # PsyAdGenesis
13
+
14
+ **Design ads that stop the scroll.**
15
 
16
  Generate high-converting ad creatives for Home Insurance and GLP-1 niches using psychological triggers and AI-powered image generation.
17
 
 
93
  - Go to https://huggingface.co/spaces
94
  - Click "Create new Space"
95
  - Choose "Docker" as the SDK
96
+ - Name your space (e.g., `your-username/psyadgenesis`)
97
 
98
  2. **Push your code** to the Space:
99
  ```bash
100
+ git clone https://huggingface.co/spaces/your-username/psyadgenesis
101
+ cd psyadgenesis
102
  # Copy your files here
103
  git add .
104
  git commit -m "Initial commit"
 
142
 
143
  Once deployed, your app will be available at:
144
  ```
145
+ https://your-username-psyadgenesis.hf.space
146
  ```
147
 
148
  ## API Endpoints
config.py CHANGED
@@ -22,7 +22,7 @@ class Settings(BaseSettings):
22
 
23
  # Database (MongoDB)
24
  mongodb_url: Optional[str] = None
25
- mongodb_db_name: str = "creative_breakthrough"
26
 
27
  # R2 Storage (Cloudflare R2)
28
  r2_endpoint: Optional[str] = None
@@ -42,7 +42,7 @@ class Settings(BaseSettings):
42
  third_flow_model: str = "gpt-4o" # Model for researcher, creative_director, designer, copywriter
43
  # Options: "gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4"
44
 
45
- # Image Generation Settings (same models as creative-breakthrough project)
46
  image_model: str = "z-image-turbo" # Z-Image Turbo - fast and high quality
47
  # Alternative models: "nano-banana", "nano-banana-pro", "imagen-4-ultra", "recraft-v3", "ideogram-v3", "photon", "seedream-3", "gpt-image-1.5"
48
  image_width: int = 1024
 
22
 
23
  # Database (MongoDB)
24
  mongodb_url: Optional[str] = None
25
+ mongodb_db_name: str = "psyadgenesis"
26
 
27
  # R2 Storage (Cloudflare R2)
28
  r2_endpoint: Optional[str] = None
 
42
  third_flow_model: str = "gpt-4o" # Model for researcher, creative_director, designer, copywriter
43
  # Options: "gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4"
44
 
45
+ # Image Generation Settings
46
  image_model: str = "z-image-turbo" # Z-Image Turbo - fast and high quality
47
  # Alternative models: "nano-banana", "nano-banana-pro", "imagen-4-ultra", "recraft-v3", "ideogram-v3", "photon", "seedream-3", "gpt-image-1.5"
48
  image_width: int = 1024
frontend/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # Ad Generator Lite - Frontend
 
 
2
 
3
  Modern Next.js dashboard for generating and managing ad creatives for Home Insurance and GLP-1 niches.
4
 
 
1
+ # PsyAdGenesis - Frontend
2
+
3
+ **Design ads that stop the scroll.**
4
 
5
  Modern Next.js dashboard for generating and managing ad creatives for Home Insurance and GLP-1 niches.
6
 
frontend/app/gallery/[id]/page.tsx CHANGED
@@ -365,29 +365,6 @@ export default function AdDetailPage() {
365
  </div>
366
  </div>
367
 
368
- {/* Primary Text */}
369
- {ad.primary_text && (
370
- <div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-cyan-500">
371
- <div className="flex items-start justify-between gap-4 mb-3">
372
- <h3 className="text-xs font-bold text-cyan-600 uppercase tracking-wider">Primary Text</h3>
373
- <div className="relative group">
374
- <Button
375
- variant="ghost"
376
- size="sm"
377
- onClick={() => handleCopyText(ad.primary_text!, "Primary Text")}
378
- className="text-cyan-500 hover:bg-cyan-50"
379
- >
380
- <Copy className="h-4 w-4" />
381
- </Button>
382
- <span className="absolute -bottom-8 left-1/2 -translate-x-1/2 px-2 py-1 bg-cyan-600 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-10">
383
- Copy Text
384
- </span>
385
- </div>
386
- </div>
387
- <p className="text-gray-700 whitespace-pre-line leading-relaxed">{ad.primary_text}</p>
388
- </div>
389
- )}
390
-
391
  {/* Description */}
392
  {ad.description && (
393
  <div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-violet-500">
 
365
  </div>
366
  </div>
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  {/* Description */}
369
  {ad.description && (
370
  <div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-violet-500">
frontend/app/gallery/page.tsx CHANGED
@@ -8,7 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
8
  import { listAds, deleteAd } from "@/lib/api/endpoints";
9
  import { useGalleryStore } from "@/store/galleryStore";
10
  import { toast } from "react-hot-toast";
11
- import { Download, Trash2, CheckSquare, Square } from "lucide-react";
12
  import type { AdFilters } from "@/types";
13
 
14
  export default function GalleryPage() {
@@ -18,10 +18,12 @@ export default function GalleryPage() {
18
  limit,
19
  offset,
20
  filters,
 
21
  selectedAds,
22
  isLoading,
23
  setAds,
24
  setFilters,
 
25
  setOffset,
26
  toggleAdSelection,
27
  clearSelection,
@@ -63,6 +65,34 @@ export default function GalleryPage() {
63
  );
64
  }
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  // Use the total from backend (it's already filtered by niche and generation_method)
67
  // For search, we show the filtered count but this is only for current page
68
  // In a production app, you'd want server-side search for accurate totals
@@ -74,7 +104,7 @@ export default function GalleryPage() {
74
  } finally {
75
  setIsLoading(false);
76
  }
77
- }, [filters, limit, offset, setAds, setIsLoading]);
78
 
79
  useEffect(() => {
80
  loadAds();
@@ -108,61 +138,84 @@ export default function GalleryPage() {
108
 
109
  return (
110
  <div className="min-h-screen pb-12">
111
- {/* Hero Section */}
112
- <div className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-cyan-50 to-pink-50 py-12 mb-8">
113
- <div className="absolute inset-0 bg-grid-pattern opacity-5"></div>
114
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
115
- <div className="text-center animate-fade-in">
116
- <h1 className="text-4xl md:text-5xl font-extrabold mb-4">
117
  <span className="gradient-text">Gallery</span>
118
  </h1>
119
- <p className="text-lg text-gray-600">
120
  {total} {total === 1 ? "ad" : "ads"} total
121
  </p>
122
  </div>
123
  </div>
124
- </div>
125
 
126
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
127
- <div className="mb-8">
128
- <div className="flex items-center justify-between">
129
- <div className="flex items-center space-x-2">
130
- {selectedAds.length > 0 && (
131
- <>
132
- <Button variant="outline" size="sm" onClick={clearSelection}>
133
- <Square className="h-4 w-4 mr-1" />
134
- Deselect ({selectedAds.length})
135
- </Button>
136
- <Button variant="danger" size="sm" onClick={handleBulkDelete}>
137
- <Trash2 className="h-4 w-4 mr-1" />
138
- Delete Selected
139
- </Button>
140
- </>
141
- )}
142
- {selectedAds.length === 0 && ads.length > 0 && (
143
- <Button variant="outline" size="sm" onClick={selectAll}>
144
- <CheckSquare className="h-4 w-4 mr-1" />
145
- Select All
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  </Button>
147
  )}
148
  </div>
149
  </div>
150
- </div>
151
 
152
  <FilterBar filters={filters} onFiltersChange={setFilters} />
153
 
154
- <div className="mt-6">
155
- <GalleryGrid
156
- ads={ads}
157
- selectedAds={selectedAds}
158
- onAdSelect={toggleAdSelection}
159
- isLoading={isLoading}
160
- />
161
- </div>
162
 
163
  {/* Pagination */}
164
  {totalPages > 1 && (
165
- <div className="mt-8 flex items-center justify-center space-x-2">
166
  <Button
167
  variant="outline"
168
  size="sm"
@@ -171,7 +224,7 @@ export default function GalleryPage() {
171
  >
172
  Previous
173
  </Button>
174
- <span className="text-sm text-gray-600">
175
  Page {currentPage} of {totalPages}
176
  </span>
177
  <Button
 
8
  import { listAds, deleteAd } from "@/lib/api/endpoints";
9
  import { useGalleryStore } from "@/store/galleryStore";
10
  import { toast } from "react-hot-toast";
11
+ import { Download, Trash2, CheckSquare, Square, ArrowUpDown } from "lucide-react";
12
  import type { AdFilters } from "@/types";
13
 
14
  export default function GalleryPage() {
 
18
  limit,
19
  offset,
20
  filters,
21
+ sortOptions,
22
  selectedAds,
23
  isLoading,
24
  setAds,
25
  setFilters,
26
+ setSortOptions,
27
  setOffset,
28
  toggleAdSelection,
29
  clearSelection,
 
65
  );
66
  }
67
 
68
+ // Sort ads by selected sort options
69
+ filteredAds.sort((a, b) => {
70
+ const { field, direction } = sortOptions;
71
+ let aValue: any = a[field as keyof typeof a];
72
+ let bValue: any = b[field as keyof typeof b];
73
+
74
+ // Handle date sorting
75
+ if (field === "created_at") {
76
+ aValue = aValue ? new Date(aValue).getTime() : 0;
77
+ bValue = bValue ? new Date(bValue).getTime() : 0;
78
+ }
79
+
80
+ // Handle string sorting (case-insensitive)
81
+ if (typeof aValue === "string" && typeof bValue === "string") {
82
+ aValue = aValue.toLowerCase();
83
+ bValue = bValue.toLowerCase();
84
+ }
85
+
86
+ // Handle null/undefined values
87
+ if (aValue == null) aValue = "";
88
+ if (bValue == null) bValue = "";
89
+
90
+ // Compare values
91
+ if (aValue < bValue) return direction === "asc" ? -1 : 1;
92
+ if (aValue > bValue) return direction === "asc" ? 1 : -1;
93
+ return 0;
94
+ });
95
+
96
  // Use the total from backend (it's already filtered by niche and generation_method)
97
  // For search, we show the filtered count but this is only for current page
98
  // In a production app, you'd want server-side search for accurate totals
 
104
  } finally {
105
  setIsLoading(false);
106
  }
107
+ }, [filters, sortOptions, limit, offset, setAds, setIsLoading]);
108
 
109
  useEffect(() => {
110
  loadAds();
 
138
 
139
  return (
140
  <div className="min-h-screen pb-12">
141
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-8">
142
+ {/* Header with title and total */}
143
+ <div className="flex items-center justify-between mb-6">
144
+ <div>
145
+ <h1 className="text-3xl md:text-4xl font-extrabold mb-2">
 
146
  <span className="gradient-text">Gallery</span>
147
  </h1>
148
+ <p className="text-sm text-gray-500">
149
  {total} {total === 1 ? "ad" : "ads"} total
150
  </p>
151
  </div>
152
  </div>
 
153
 
154
+ {/* Actions Bar */}
155
+ <div className="mb-6">
156
+ <div className="flex items-center justify-between flex-wrap gap-4">
157
+ <div className="flex items-center gap-3">
158
+ {selectedAds.length > 0 ? (
159
+ <>
160
+ <Button variant="outline" size="sm" onClick={clearSelection}>
161
+ <div className="flex items-center gap-2">
162
+ <Square className="h-4 w-4" />
163
+ <span>Deselect ({selectedAds.length})</span>
164
+ </div>
165
+ </Button>
166
+ <Button variant="danger" size="sm" onClick={handleBulkDelete}>
167
+ <div className="flex items-center gap-2">
168
+ <Trash2 className="h-4 w-4" />
169
+ <span>Delete Selected</span>
170
+ </div>
171
+ </Button>
172
+ </>
173
+ ) : (
174
+ ads.length > 0 && (
175
+ <Button variant="outline" size="sm" onClick={selectAll}>
176
+ <div className="flex items-center gap-2">
177
+ <CheckSquare className="h-4 w-4" />
178
+ <span>Select All</span>
179
+ </div>
180
+ </Button>
181
+ )
182
+ )}
183
+ </div>
184
+
185
+ {/* Sort Controls */}
186
+ {sortOptions && (
187
+ <Button
188
+ variant="outline"
189
+ size="sm"
190
+ onClick={() => setSortOptions({
191
+ field: "created_at",
192
+ direction: sortOptions.direction === "desc" ? "asc" : "desc"
193
+ })}
194
+ className="flex items-center gap-2"
195
+ >
196
+ <ArrowUpDown className="h-4 w-4" />
197
+ <span className="text-sm">
198
+ {sortOptions.direction === "desc" ? "Newest First" : "Oldest First"}
199
+ </span>
200
  </Button>
201
  )}
202
  </div>
203
  </div>
 
204
 
205
  <FilterBar filters={filters} onFiltersChange={setFilters} />
206
 
207
+ <div className="mt-6">
208
+ <GalleryGrid
209
+ ads={ads}
210
+ selectedAds={selectedAds}
211
+ onAdSelect={toggleAdSelection}
212
+ isLoading={isLoading}
213
+ />
214
+ </div>
215
 
216
  {/* Pagination */}
217
  {totalPages > 1 && (
218
+ <div className="mt-8 flex items-center justify-center gap-4">
219
  <Button
220
  variant="outline"
221
  size="sm"
 
224
  >
225
  Previous
226
  </Button>
227
+ <span className="text-sm text-gray-600 font-medium">
228
  Page {currentPage} of {totalPages}
229
  </span>
230
  <Button
frontend/app/layout.tsx CHANGED
@@ -10,8 +10,8 @@ const inter = Inter({
10
  });
11
 
12
  export const metadata: Metadata = {
13
- title: "Creative Breakthrough",
14
- description: "Generate high-converting ad creatives for Home Insurance and GLP-1 niches",
15
  };
16
 
17
  export default function RootLayout({
 
10
  });
11
 
12
  export const metadata: Metadata = {
13
+ title: "PsyAdGenesis",
14
+ description: "Design ads that stop the scroll. Generate high-converting ad creatives for Home Insurance and GLP-1 niches using psychological triggers and AI-powered image generation.",
15
  };
16
 
17
  export default function RootLayout({
frontend/app/page.tsx CHANGED
@@ -155,11 +155,13 @@ export default function Dashboard() {
155
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
156
  <div className="text-center animate-fade-in">
157
  <h1 className="text-5xl md:text-6xl font-extrabold mb-4">
158
- <span className="gradient-text">Creative</span>
159
- <span className="text-gray-900"> Breakthrough</span>
160
  </h1>
161
- <p className="text-xl text-gray-600 max-w-2xl mx-auto">
162
- Create high-converting ad creatives for Home Insurance and GLP-1 niches with AI-powered generation
 
 
 
163
  </p>
164
  </div>
165
  </div>
 
155
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
156
  <div className="text-center animate-fade-in">
157
  <h1 className="text-5xl md:text-6xl font-extrabold mb-4">
158
+ <span className="gradient-text">PsyAdGenesis</span>
 
159
  </h1>
160
+ <p className="text-xl text-gray-600 max-w-2xl mx-auto font-medium">
161
+ Design ads that stop the scroll.
162
+ </p>
163
+ <p className="text-lg text-gray-500 max-w-2xl mx-auto mt-2">
164
+ Generate high-converting ad creatives for Home Insurance and GLP-1 niches with AI-powered generation
165
  </p>
166
  </div>
167
  </div>
frontend/components/generation/AdPreview.tsx CHANGED
@@ -261,29 +261,6 @@ export const AdPreview: React.FC<AdPreviewProps> = ({ ad }) => {
261
  </h1>
262
  </div>
263
 
264
- {/* Primary Text */}
265
- {ad.primary_text && (
266
- <div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-cyan-500">
267
- <div className="flex items-start justify-between gap-4 mb-3">
268
- <h3 className="text-xs font-bold text-cyan-600 uppercase tracking-wider">Primary Text</h3>
269
- <div className="relative group">
270
- <Button
271
- variant="ghost"
272
- size="sm"
273
- onClick={() => handleCopyText(ad.primary_text!, "Primary Text")}
274
- className="text-cyan-500 hover:bg-cyan-50"
275
- >
276
- <Copy className="h-4 w-4" />
277
- </Button>
278
- <span className="absolute -bottom-8 left-1/2 -translate-x-1/2 px-2 py-1 bg-cyan-600 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-10">
279
- Copy Text
280
- </span>
281
- </div>
282
- </div>
283
- <p className="text-gray-700 whitespace-pre-line leading-relaxed">{ad.primary_text}</p>
284
- </div>
285
- )}
286
-
287
  {/* Description */}
288
  {ad.description && (
289
  <div className="bg-white rounded-2xl shadow-md p-6 border-l-4 border-violet-500">
 
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">
frontend/components/layout/Header.tsx CHANGED
@@ -35,7 +35,7 @@ export const Header: React.FC = () => {
35
  <div className="absolute inset-0 bg-blue-500/20 rounded-full blur-xl group-hover:bg-cyan-500/20 transition-colors duration-300"></div>
36
  </div>
37
  <span className="text-2xl font-bold gradient-text group-hover:scale-105 transition-transform duration-300">
38
- Creative Breakthrough
39
  </span>
40
  </Link>
41
  </div>
@@ -77,10 +77,11 @@ export const Header: React.FC = () => {
77
  variant="outline"
78
  size="sm"
79
  onClick={handleLogout}
80
- className="flex items-center space-x-2"
81
  >
82
- <LogOut className="h-4 w-4" />
83
- <span className="hidden sm:inline">Logout</span>
 
 
84
  </Button>
85
  </div>
86
  ) : (
 
35
  <div className="absolute inset-0 bg-blue-500/20 rounded-full blur-xl group-hover:bg-cyan-500/20 transition-colors duration-300"></div>
36
  </div>
37
  <span className="text-2xl font-bold gradient-text group-hover:scale-105 transition-transform duration-300">
38
+ PsyAdGenesis
39
  </span>
40
  </Link>
41
  </div>
 
77
  variant="outline"
78
  size="sm"
79
  onClick={handleLogout}
 
80
  >
81
+ <div className="flex items-center space-x-2">
82
+ <LogOut className="h-4 w-4" />
83
+ <span className="hidden sm:inline">Logout</span>
84
+ </div>
85
  </Button>
86
  </div>
87
  ) : (
frontend/store/galleryStore.ts CHANGED
@@ -50,7 +50,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
50
  offset: 0, // Reset to first page when filters change
51
  })),
52
 
53
- setSortOptions: (sort) => set({ sortOptions: sort }),
54
 
55
  toggleAdSelection: (adId) => set((state) => ({
56
  selectedAds: state.selectedAds.includes(adId)
 
50
  offset: 0, // Reset to first page when filters change
51
  })),
52
 
53
+ setSortOptions: (sort) => set({ sortOptions: sort, offset: 0 }), // Reset to first page when sort changes
54
 
55
  toggleAdSelection: (adId) => set((state) => ({
56
  selectedAds: state.selectedAds.includes(adId)
main.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Ad Generator Lite - FastAPI Application
3
- Simplified winning ads generator for Home Insurance and GLP-1 niches
4
  Saves all ads to Neon PostgreSQL database with image URLs
5
  """
6
 
@@ -40,7 +40,7 @@ api_logger = logging.getLogger("api")
40
  async def lifespan(app: FastAPI):
41
  """Startup and shutdown events."""
42
  # Startup: Connect to database
43
- print("Starting Ad Generator Lite...")
44
  await db_service.connect()
45
  yield
46
  # Shutdown: Disconnect from database
@@ -50,8 +50,8 @@ async def lifespan(app: FastAPI):
50
 
51
  # Create FastAPI app with lifespan for database connection
52
  app = FastAPI(
53
- title="Ad Generator Lite",
54
- description="Generate high-converting ad creatives for Home Insurance and GLP-1 niches using psychological triggers. All ads are saved to Neon database.",
55
  version="2.0.0",
56
  lifespan=lifespan,
57
  )
@@ -72,7 +72,7 @@ if os.getenv("CORS_ORIGINS"):
72
 
73
  # For Hugging Face Spaces, use regex to match any .hf.space domain
74
  # Note: If deploying to HF Spaces, add your Space URL to CORS_ORIGINS env var
75
- # Example: CORS_ORIGINS=https://your-username-ad-generator-lite.hf.space
76
 
77
  app.add_middleware(
78
  CORSMiddleware,
@@ -315,9 +315,9 @@ class TestingMatrixResponse(BaseModel):
315
  async def api_info():
316
  """API info endpoint."""
317
  return {
318
- "name": "Ad Generator Lite",
319
  "version": "2.0.0",
320
- "description": "Generate high-converting ads using Angle × Concept matrix system",
321
  "endpoints": {
322
  "POST /generate": "Generate single ad (original mode)",
323
  "POST /generate/batch": "Generate multiple ads (original mode)",
 
1
  """
2
+ PsyAdGenesis - FastAPI Application
3
+ Design ads that stop the scroll. Generate high-converting ad creatives for Home Insurance and GLP-1 niches
4
  Saves all ads to Neon PostgreSQL database with image URLs
5
  """
6
 
 
40
  async def lifespan(app: FastAPI):
41
  """Startup and shutdown events."""
42
  # Startup: Connect to database
43
+ print("Starting PsyAdGenesis...")
44
  await db_service.connect()
45
  yield
46
  # Shutdown: Disconnect from database
 
50
 
51
  # Create FastAPI app with lifespan for database connection
52
  app = FastAPI(
53
+ title="PsyAdGenesis",
54
+ description="Design ads that stop the scroll. Generate high-converting ad creatives for Home Insurance and GLP-1 niches using psychological triggers and AI-powered image generation.",
55
  version="2.0.0",
56
  lifespan=lifespan,
57
  )
 
72
 
73
  # For Hugging Face Spaces, use regex to match any .hf.space domain
74
  # Note: If deploying to HF Spaces, add your Space URL to CORS_ORIGINS env var
75
+ # Example: CORS_ORIGINS=https://your-username-psyadgenesis.hf.space
76
 
77
  app.add_middleware(
78
  CORSMiddleware,
 
315
  async def api_info():
316
  """API info endpoint."""
317
  return {
318
+ "name": "PsyAdGenesis",
319
  "version": "2.0.0",
320
+ "description": "Design ads that stop the scroll. Generate high-converting ads using Angle × Concept matrix system",
321
  "endpoints": {
322
  "POST /generate": "Generate single ad (original mode)",
323
  "POST /generate/batch": "Generate multiple ads (original mode)",
services/database.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- Database Service for Ad Generator Lite
3
  Handles MongoDB connection and CRUD operations
4
  """
5
 
 
1
  """
2
+ Database Service for PsyAdGenesis
3
  Handles MongoDB connection and CRUD operations
4
  """
5
 
services/generator.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
  Main Ad Generator Service
3
  Combines LLM + Image generation with maximum randomization for variety
4
- Uses professional prompting techniques from creative-breakthrough project
5
  Saves ad creatives to Neon database with image URLs
6
  """
7
 
 
1
  """
2
  Main Ad Generator Service
3
  Combines LLM + Image generation with maximum randomization for variety
4
+ Uses professional prompting techniques for PsyAdGenesis
5
  Saves ad creatives to Neon database with image URLs
6
  """
7
 
services/image.py CHANGED
@@ -19,7 +19,7 @@ from openai import OpenAI
19
  from config import settings
20
 
21
 
22
- # Model registry - same as original creative-breakthrough project
23
  MODEL_REGISTRY: Dict[str, Dict[str, Any]] = {
24
  "nano-banana": {
25
  "id": "google/nano-banana",
 
19
  from config import settings
20
 
21
 
22
+ # Model registry for PsyAdGenesis
23
  MODEL_REGISTRY: Dict[str, Dict[str, Any]] = {
24
  "nano-banana": {
25
  "id": "google/nano-banana",
services/r2_storage.py CHANGED
@@ -45,7 +45,7 @@ class R2StorageService:
45
  region_name='auto', # R2 doesn't use regions
46
  )
47
  self.bucket_name = settings.r2_bucket_name
48
- self.folder = "creative-breakthrough"
49
 
50
  def upload_image(
51
  self,
 
45
  region_name='auto', # R2 doesn't use regions
46
  )
47
  self.bucket_name = settings.r2_bucket_name
48
+ self.folder = "psyadgenesis"
49
 
50
  def upload_image(
51
  self,