sammoftah commited on
Commit
272acb6
Β·
verified Β·
1 Parent(s): 987baa0

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -140
app.py CHANGED
@@ -128,142 +128,145 @@ def build_event_sections(
128
  time_desc: str,
129
  season_desc: str,
130
  mood: str,
 
 
 
131
  ) -> Dict[str, str]:
132
- """Builds prompt sections focusing on event, location, year, and time."""
133
- vocab = get_era_vocabulary(event.get("year") or 0)
134
- region_ctx = get_region_context((event.get("facets") or {}).get("region"))
 
 
135
 
136
- # Extract event details
137
  event_name = event.get("name", "Historical scene")
138
- event_year = event.get("year", 0)
139
- narrative = (event.get("narrative") or event.get("summary") or event.get("description") or "").strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  actors = event.get("actors") or []
141
-
142
- # Get location context
143
- region_name = (event.get("facets") or {}).get("region", "")
144
- architecture = region_ctx.get("architecture") or vocab.get("architecture", "")
145
-
146
- # Build year/era marker (prominent)
147
- if event_year < 0:
148
- era_marker = f"{abs(event_year)} BCE"
149
- elif event_year < 500:
150
- era_marker = "ancient era"
151
- elif event_year < 1500:
152
- era_marker = "medieval era"
153
- elif event_year < 1800:
154
- era_marker = "early modern era"
155
- elif event_year < 1900:
156
- era_marker = "19th century"
157
- else:
158
- era_marker = f"{event_year}"
159
-
160
- # Get location name/region (prioritize location)
161
- location_desc = ""
162
- if region_name:
163
- region_map = {
164
- "western_europe": "Western Europe",
165
- "eastern_europe": "Eastern Europe",
166
- "north_america": "North America",
167
- "south_america": "South America",
168
- "east_asia": "East Asia",
169
- "middle_east": "Middle East",
170
- "africa": "Africa",
171
- }
172
- location_desc = region_map.get(region_name.lower(), region_name.replace("_", " ").title())
173
-
174
- # Simplify architecture description
175
- architecture_lower = architecture.lower() if architecture else ""
176
- if "roman" in architecture_lower:
177
- arch_short = "Roman architecture"
178
- elif "gothic" in architecture_lower:
179
- arch_short = "Gothic architecture"
180
- elif "medieval" in architecture_lower:
181
- arch_short = "medieval architecture"
182
- elif "asia" in region_name.lower() and event_year < 1900:
183
- arch_short = "traditional Asian architecture"
184
- else:
185
- arch_short = architecture or "period architecture"
186
-
187
- # Extract key action from narrative (concise)
188
- key_action = ""
189
- if narrative:
190
- # Get first short sentence or phrase
191
- sentences = [s.strip() for s in narrative.split(".") if len(s.strip()) > 10 and len(s.strip()) < 80]
192
- if sentences:
193
- key_action = sentences[0]
194
- # Trim if too long
195
- if len(key_action) > 60:
196
- key_action = key_action[:57] + "..."
197
-
198
- # Build focused scene: [Event] at [Location] in [Year] at [Time]: [Action]
199
- # Make time prominent - include it in the main sentence
200
- time_short = time_desc.split(",")[0] if "," in time_desc else time_desc # Get first part of time description
201
-
202
- if location_desc:
203
- if key_action:
204
- scene_sentence = f"{event_name} at {location_desc} in {era_marker} at {time_short}: {key_action}"
205
- else:
206
- scene_sentence = f"{event_name} at {location_desc} in {era_marker} at {time_short}"
207
- else:
208
- if key_action:
209
- scene_sentence = f"{event_name} in {era_marker} at {time_short}: {key_action}"
210
- else:
211
- scene_sentence = f"{event_name} in {era_marker} at {time_short}"
212
-
213
- # Essential participants only (concise)
214
- if actors and len(actors) > 0:
215
- main_actors = actors[:2] # Limit to 2 most important
216
- if len(main_actors) == 1:
217
- participants_sentence = f"{main_actors[0]} present"
218
- else:
219
- participants_sentence = f"{main_actors[0]} and {main_actors[1]} present"
220
- else:
221
- participants_sentence = ""
222
-
223
- # Location architecture (concise, time already in main sentence)
224
- if location_desc and arch_short:
225
- location_sentence = f"{location_desc} with {arch_short}"
226
- elif arch_short:
227
- location_sentence = arch_short
228
  else:
229
- location_sentence = ""
230
-
 
 
 
 
 
 
 
 
 
 
231
  return {
232
- "event_location_year": scene_sentence, # Core: Event + Location + Year
233
- "participants": participants_sentence,
234
- "location_setting": location_sentence,
 
 
 
235
  }
236
 
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  def assemble_prompt_from_sections(sections: Dict[str, str], quality: str) -> str:
239
- """Combines prompt sections into final prompt, keeping it 40-60 words."""
240
- # Focused structure: [Event at Location in Year] [Participants] [Location/Setting] [Style/Quality]
241
- parts = [
242
- sections.get("event_location_year"), # Core: Event + Location + Year (most important)
243
- sections.get("participants"), # Essential participants only
244
- sections.get("location_setting"), # Location architecture and time
245
- ]
246
- body = ". ".join(part for part in parts if part)
247
-
248
- # Add style/mood and quality tags at end (concise)
249
- style_quality = f"{quality}"
250
- result = f"{body}. {style_quality}".strip()
251
-
252
- # Ensure prompt is 40-60 words (target ~50 words)
253
- word_count = len(result.split())
254
- if word_count > 65:
255
- # Trim if too long - keep core elements, reduce quality tags
256
- words = result.split()
257
- # Keep first 45 words (core content), add essential quality
258
- core = " ".join(words[:45])
259
- essential_quality = "historically accurate, photorealistic"
260
- result = f"{core}. {essential_quality}"
261
- elif word_count < 35:
262
- # Expand slightly if too short - add more context
263
- if not sections.get("participants"):
264
- result = f"{body}. Historical figures visible. {style_quality}".strip()
265
-
266
- return result
 
 
 
 
 
 
 
 
 
 
267
 
268
 
269
  def build_fallback_prompt(
@@ -300,7 +303,7 @@ def build_fallback_prompt(
300
  "Glitch": "digital glitch art style, cyberpunk aesthetic, data corruption effects, historically themed"
301
  }
302
  quality = quality_map.get(mood, f"{mood} style, historically inspired")
303
- else:
304
  quality = f"{mood} style, historically accurate, photorealistic, 8K"
305
  prompt = assemble_prompt_from_sections(sections, quality)
306
  hint = (
@@ -344,14 +347,14 @@ print("πŸ”‘ API Token Status:")
344
  print("="*60)
345
  if HF_TOKEN:
346
  print(f"βœ… HF_TOKEN: Found ({len(HF_TOKEN)} chars) - {HF_TOKEN[:10]}...")
347
- else:
348
  print("❌ HF_TOKEN: Not found!")
349
  print(" Set HUGGINGFACE_API_TOKEN or HF_TOKEN environment variable")
350
 
351
  if GEMINI_API_KEY:
352
  print(f"βœ… GEMINI_API_KEY: Found ({len(GEMINI_API_KEY)} chars) - {GEMINI_API_KEY[:10]}...")
353
  genai.configure(api_key=GEMINI_API_KEY)
354
- else:
355
  print("⚠️ GEMINI_API_KEY: Not found (prompts will use fallback)")
356
  print("="*60 + "\n")
357
 
@@ -420,7 +423,7 @@ def generate_historical_prompt(
420
 
421
  if events:
422
  focus_event = events[0]
423
- sections = build_event_sections(focus_event, time_desc, season_desc, mood)
424
  # Style-appropriate quality tags
425
  if mood in ["Cartoon", "Minecraft", "Retro", "Glitch"]:
426
  quality_map = {
@@ -430,7 +433,7 @@ def generate_historical_prompt(
430
  "Glitch": "digital glitch art style, cyberpunk aesthetic, data corruption effects, historically themed"
431
  }
432
  quality = quality_map.get(mood, f"{mood} style, historically inspired")
433
- else:
434
  quality = f"{mood} style, historically accurate, photorealistic, 8K"
435
  prompt = assemble_prompt_from_sections(sections, quality)
436
 
@@ -452,7 +455,7 @@ def generate_historical_prompt(
452
  year_match = f"πŸ“… ~{year_delta} years"
453
  elif year_delta <= 10:
454
  year_match = f"πŸ“… ~{year_delta} years apart"
455
- else:
456
  year_match = f"⚠️ {year_delta} years apart"
457
 
458
  hint_lines = [
@@ -555,7 +558,7 @@ def process_coordinates(lat: float, lon: float, year: int, month: int, day: int,
555
  prompt = custom_prompt.strip()
556
  hint = f"πŸ“ {lat:.4f}, {lon:.4f} | πŸ“… {year}-{month:02d}-{day:02d} {hour}:00 (Custom prompt)"
557
  status_parts.append("βœ… Using custom prompt")
558
- else:
559
  status_parts.append("πŸ” Searching historical events...")
560
  prompt, hint = generate_historical_prompt(lat, lon, year, month, day, hour, mood)
561
  status_parts.append("βœ… Prompt generated")
@@ -592,9 +595,9 @@ def process_coordinates(lat: float, lon: float, year: int, month: int, day: int,
592
  year_badge = "🎯"
593
  elif year_delta <= 5:
594
  year_badge = "πŸ“…"
595
- else:
596
  year_badge = "⏳"
597
-
598
  timeline_md += (
599
  f"**{event.get('year')}** {year_badge} β€” {source_icon} {event.get('name')}{qid_link} "
600
  f"({event.get('distance_km')}km"
@@ -611,7 +614,7 @@ def process_coordinates(lat: float, lon: float, year: int, month: int, day: int,
611
  participants = event.get("actors") or event.get("participants") or []
612
  if participants and source == "wikidata":
613
  timeline_md += f"_Participants: {', '.join(participants[:4])}_\n\n"
614
- else:
615
  timeline_md += "_No specific events found in database or Wikidata. Scene generated from era-appropriate context._"
616
 
617
  return image, prompt, hint, status, timeline_md
@@ -1065,9 +1068,9 @@ def create_app():
1065
  )
1066
 
1067
  gr.Markdown("### πŸ–ΌοΈ Generated Image")
1068
- image_output = gr.Image(
1069
- label="",
1070
- show_label=False,
1071
  height=600,
1072
  type="pil"
1073
  )
@@ -1147,7 +1150,7 @@ def create_app():
1147
  f"🧭 Parsed from prompt (confidence {parsed.confidence:.2f}) Β· "
1148
  f"{lat_val:.4f}, {lon_val:.4f}, year {year_val}"
1149
  )
1150
- else:
1151
  derived_note = "⚠️ Could not confidently parse prompt context; using manual inputs."
1152
 
1153
  image, prompt, hint, status, timeline = process_coordinates(
 
128
  time_desc: str,
129
  season_desc: str,
130
  mood: str,
131
+ lat: float,
132
+ lon: float,
133
+ year: int,
134
  ) -> Dict[str, str]:
135
+ """Build VISUAL-FIRST event description for image generation.
136
+
137
+ Returns concrete visual elements that image models understand.
138
+ Target: 35-50 words for base prompt (style adds 15-20 more).
139
+ """
140
 
 
141
  event_name = event.get("name", "Historical scene")
142
+ event_year = event.get("year", year)
143
+ location = get_location_name(event, lat, lon)
144
+
145
+ # VISUAL SUBJECT (what the image shows)
146
+ subject_type = "historical scene"
147
+ if "battle" in event_name.lower() or "war" in event_name.lower():
148
+ subject_type = "battlefield"
149
+ elif "signing" in event_name.lower() or "declaration" in event_name.lower():
150
+ subject_type = "formal ceremony"
151
+ elif "speech" in event_name.lower() or "address" in event_name.lower():
152
+ subject_type = "public gathering"
153
+ elif "fall" in event_name.lower() or "liberation" in event_name.lower():
154
+ subject_type = "crowd scene"
155
+
156
+ # PARTICIPANTS (who's in the image with period clothing)
157
  actors = event.get("actors") or []
158
+ participants_desc = ""
159
+ if actors:
160
+ # Add period-specific clothing descriptors
161
+ clothing = get_period_clothing(event_year)
162
+ if len(actors) == 1:
163
+ participants_desc = f"{actors[0]} in {clothing}"
164
+ elif len(actors) == 2:
165
+ participants_desc = f"{actors[0]} and {actors[1]} in {clothing}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  else:
167
+ participants_desc = f"{actors[0]}, {actors[1]}, and others in {clothing}"
168
+
169
+ # ENVIRONMENT (where the scene takes place)
170
+ location_desc = get_environment_description(location, event_name, event_year)
171
+
172
+ # VISUAL ELEMENTS (period artifacts, architecture)
173
+ artifacts = event.get("artifacts") or []
174
+ visual_elements = join_list(artifacts[:3], "and") if artifacts else ""
175
+
176
+ # LIGHTING (time-based atmospheric description)
177
+ lighting = time_desc # Use full description
178
+
179
  return {
180
+ "subject": subject_type,
181
+ "participants": participants_desc,
182
+ "location": location_desc,
183
+ "elements": visual_elements,
184
+ "lighting": lighting,
185
+ "event_name": event_name, # Keep for fallback
186
  }
187
 
188
 
189
+ def get_period_clothing(year: int) -> str:
190
+ """Get period-appropriate clothing description."""
191
+ if year < 1500:
192
+ return "medieval robes and tunics"
193
+ elif year < 1700:
194
+ return "Renaissance doublets and robes"
195
+ elif year < 1800:
196
+ return "18th century coats and breeches"
197
+ elif year < 1850:
198
+ return "early 19th century military uniforms"
199
+ elif year < 1900:
200
+ return "Victorian formal attire"
201
+ elif year < 1920:
202
+ return "Edwardian formal dress"
203
+ elif year < 1950:
204
+ return "1940s military uniforms"
205
+ elif year < 1980:
206
+ return "mid-century formal wear"
207
+ else:
208
+ return "modern formal attire"
209
+
210
+
211
+ def get_environment_description(location: str, event_name: str, year: int) -> str:
212
+ """Get concrete environmental description."""
213
+ if "Β°" in location:
214
+ env_name = ""
215
+ else:
216
+ env_name = location
217
+
218
+ if "battle" in event_name.lower() or "war" in event_name.lower():
219
+ if year < 1900:
220
+ return f"{env_name} countryside with period cannons, military encampments, and smoke from musket fire" if env_name else "muddy battlefield with period cannons, military encampments, and gunpowder smoke"
221
+ else:
222
+ return f"{env_name} terrain with military vehicles, fortifications, and artillery" if env_name else "war-torn terrain with military vehicles, fortifications, and artillery smoke"
223
+ elif "palace" in location.lower() or "hall" in event_name.lower():
224
+ return "grand ornate interior with period chandeliers, decorative architecture, and formal furnishings"
225
+ elif "street" in event_name.lower() or "crowd" in event_name.lower() or "fall" in event_name.lower():
226
+ return f"{env_name} streets with period buildings, gathered crowds, and urban architecture" if env_name else "city streets with period buildings, gathered crowds, and architectural details"
227
+ else:
228
+ return f"{env_name} with historically accurate period architecture and setting" if env_name else "period-accurate setting with appropriate historical architecture"
229
+
230
+
231
  def assemble_prompt_from_sections(sections: Dict[str, str], quality: str) -> str:
232
+ """Assemble VISUAL-FIRST prompt for image generation.
233
+
234
+ Format: "[Subject] showing [participants]. [Environment with elements]. [Lighting]."
235
+ NO metadata - only visual descriptions that models understand.
236
+ """
237
+
238
+ subject = sections.get("subject", "historical scene")
239
+ participants = sections.get("participants", "")
240
+ location = sections.get("location", "")
241
+ elements = sections.get("elements", "")
242
+ lighting = sections.get("lighting", "")
243
+
244
+ # Build prompt sentence by sentence
245
+ parts = []
246
+
247
+ # Sentence 1: Subject + Participants
248
+ if participants:
249
+ parts.append(f"{subject.capitalize()} showing {participants}")
250
+ else:
251
+ event_name = sections.get("event_name", "historical event")
252
+ parts.append(f"{subject.capitalize()} depicting {event_name}")
253
+
254
+ # Sentence 2: Environment WITH Elements (combined)
255
+ if location and elements:
256
+ parts.append(f"{location} with {elements}")
257
+ elif location:
258
+ parts.append(location)
259
+ elif elements:
260
+ parts.append(f"Scene with {elements}")
261
+
262
+ # Sentence 3: Lighting/Atmosphere
263
+ if lighting:
264
+ parts.append(f"{lighting}")
265
+
266
+ # Join all parts with periods for clear structure
267
+ final = ". ".join(parts)
268
+
269
+ return final.strip()
270
 
271
 
272
  def build_fallback_prompt(
 
303
  "Glitch": "digital glitch art style, cyberpunk aesthetic, data corruption effects, historically themed"
304
  }
305
  quality = quality_map.get(mood, f"{mood} style, historically inspired")
306
+ else:
307
  quality = f"{mood} style, historically accurate, photorealistic, 8K"
308
  prompt = assemble_prompt_from_sections(sections, quality)
309
  hint = (
 
347
  print("="*60)
348
  if HF_TOKEN:
349
  print(f"βœ… HF_TOKEN: Found ({len(HF_TOKEN)} chars) - {HF_TOKEN[:10]}...")
350
+ else:
351
  print("❌ HF_TOKEN: Not found!")
352
  print(" Set HUGGINGFACE_API_TOKEN or HF_TOKEN environment variable")
353
 
354
  if GEMINI_API_KEY:
355
  print(f"βœ… GEMINI_API_KEY: Found ({len(GEMINI_API_KEY)} chars) - {GEMINI_API_KEY[:10]}...")
356
  genai.configure(api_key=GEMINI_API_KEY)
357
+ else:
358
  print("⚠️ GEMINI_API_KEY: Not found (prompts will use fallback)")
359
  print("="*60 + "\n")
360
 
 
423
 
424
  if events:
425
  focus_event = events[0]
426
+ sections = build_event_sections(focus_event, time_desc, season_desc, mood, lat, lon, year)
427
  # Style-appropriate quality tags
428
  if mood in ["Cartoon", "Minecraft", "Retro", "Glitch"]:
429
  quality_map = {
 
433
  "Glitch": "digital glitch art style, cyberpunk aesthetic, data corruption effects, historically themed"
434
  }
435
  quality = quality_map.get(mood, f"{mood} style, historically inspired")
436
+ else:
437
  quality = f"{mood} style, historically accurate, photorealistic, 8K"
438
  prompt = assemble_prompt_from_sections(sections, quality)
439
 
 
455
  year_match = f"πŸ“… ~{year_delta} years"
456
  elif year_delta <= 10:
457
  year_match = f"πŸ“… ~{year_delta} years apart"
458
+ else:
459
  year_match = f"⚠️ {year_delta} years apart"
460
 
461
  hint_lines = [
 
558
  prompt = custom_prompt.strip()
559
  hint = f"πŸ“ {lat:.4f}, {lon:.4f} | πŸ“… {year}-{month:02d}-{day:02d} {hour}:00 (Custom prompt)"
560
  status_parts.append("βœ… Using custom prompt")
561
+ else:
562
  status_parts.append("πŸ” Searching historical events...")
563
  prompt, hint = generate_historical_prompt(lat, lon, year, month, day, hour, mood)
564
  status_parts.append("βœ… Prompt generated")
 
595
  year_badge = "🎯"
596
  elif year_delta <= 5:
597
  year_badge = "πŸ“…"
598
+ else:
599
  year_badge = "⏳"
600
+
601
  timeline_md += (
602
  f"**{event.get('year')}** {year_badge} β€” {source_icon} {event.get('name')}{qid_link} "
603
  f"({event.get('distance_km')}km"
 
614
  participants = event.get("actors") or event.get("participants") or []
615
  if participants and source == "wikidata":
616
  timeline_md += f"_Participants: {', '.join(participants[:4])}_\n\n"
617
+ else:
618
  timeline_md += "_No specific events found in database or Wikidata. Scene generated from era-appropriate context._"
619
 
620
  return image, prompt, hint, status, timeline_md
 
1068
  )
1069
 
1070
  gr.Markdown("### πŸ–ΌοΈ Generated Image")
1071
+ image_output = gr.Image(
1072
+ label="",
1073
+ show_label=False,
1074
  height=600,
1075
  type="pil"
1076
  )
 
1150
  f"🧭 Parsed from prompt (confidence {parsed.confidence:.2f}) Β· "
1151
  f"{lat_val:.4f}, {lon_val:.4f}, year {year_val}"
1152
  )
1153
+ else:
1154
  derived_note = "⚠️ Could not confidently parse prompt context; using manual inputs."
1155
 
1156
  image, prompt, hint, status, timeline = process_coordinates(