| import gradio as gr
|
| import requests
|
| import os
|
| import io
|
| from PIL import Image
|
|
|
| API_URL = "http://127.0.0.1:8000"
|
|
|
| def ui_wrapper(user_prompt, progress=gr.Progress()):
|
| """Wrapper that calls the API for generation with progress tracking."""
|
| try:
|
| if not user_prompt or not user_prompt.strip():
|
| raise gr.Error("Please enter a subject to generate an archive.")
|
|
|
|
|
| progress(0.1, desc="🔍 Analyzing your request...")
|
|
|
|
|
| progress(0.3, desc="🎨 Generating historical archive... (this may take 1-2 minutes)")
|
|
|
|
|
| response = requests.post(f"{API_URL}/agent/generate", json={
|
| "prompt": user_prompt,
|
| "use_curator": True
|
| }, timeout=120)
|
|
|
|
|
|
|
|
|
| if response.status_code == 200:
|
|
|
| progress(1.0, desc="✅ Generated! Indexing in background...")
|
| return Image.open(io.BytesIO(response.content))
|
| elif response.status_code == 400:
|
| raise gr.Error("Invalid prompt. Try describing a historical subject or scene.")
|
| elif response.status_code == 500:
|
| raise gr.Error("The archive server is experiencing issues. Please try again in a moment.")
|
| else:
|
| raise gr.Error("Could not generate archive. The server may be busy or offline.")
|
|
|
| except requests.exceptions.Timeout:
|
| raise gr.Error("⏱️ Generation is taking longer than expected. Try a simpler subject or check back later.")
|
| except requests.exceptions.ConnectionError:
|
| raise gr.Error("❌ Could not connect to the archive server. Make sure `api.py` is running on port 8000.")
|
| except gr.Error:
|
| raise
|
| except Exception as e:
|
| raise gr.Error(f"Unexpected error: {str(e)}")
|
|
|
| def search_wrapper(query):
|
| """Calls API for semantic search with improved error handling."""
|
| try:
|
| if not query or not query.strip():
|
| raise gr.Error("Please enter search keywords to explore the archive.")
|
|
|
| response = requests.post(f"{API_URL}/curator/search", json={
|
| "query": query,
|
| "limit": 9
|
| }, timeout=30)
|
|
|
| if response.status_code == 200:
|
| data = response.json()
|
|
|
| gallery_items = []
|
| for item in data.get("results", []):
|
|
|
| gallery_items.append((item['path'], item['tags']))
|
|
|
| if not gallery_items:
|
| gr.Info("🔍 No artifacts found. Try different keywords or broader terms.")
|
|
|
| return gallery_items
|
| else:
|
| print(f"Search API Error: {response.text}")
|
| raise gr.Error("Search failed. The archive server may be offline.")
|
|
|
| except requests.exceptions.Timeout:
|
| raise gr.Error("Search is taking too long. Please try again.")
|
| except requests.exceptions.ConnectionError:
|
| raise gr.Error("Cannot connect to archive. Make sure `api.py` is running.")
|
| except gr.Error:
|
| raise
|
| except Exception as e:
|
| print(f"Search failed: {e}")
|
| raise gr.Error(f"Search error: {str(e)}")
|
|
|
|
|
| custom_css = """
|
| button {
|
| transition: all 0.2s ease-in-out !important;
|
| }
|
|
|
| button:hover {
|
| transform: scale(1.02);
|
| opacity: 0.95;
|
| }
|
|
|
| button:active {
|
| transform: scale(0.98);
|
| }
|
|
|
| button:focus-visible,
|
| textarea:focus-visible,
|
| input:focus-visible {
|
| outline: 2px solid #8B5CF6 !important;
|
| outline-offset: 2px !important;
|
| }
|
|
|
| /* Responsive gallery spacing */
|
| @media (max-width: 768px) {
|
| .gallery {
|
| gap: 0.5rem !important;
|
| }
|
| }
|
| """
|
|
|
| with gr.Blocks(title="Cora — Historical Archive Generator") as app:
|
|
|
| gr.Markdown(
|
| """
|
| # 🏛️ Cora — The Archive Curator
|
|
|
| **Generate AI-powered historical illustrations** using SDXL and Visual RAG.
|
| Transform simple prompts into detailed historical scenes, artifacts, and engravings.
|
|
|
| **How it works**: Your prompt is enhanced by an AI curator, then rendered as a historically-themed illustration and archived for future semantic search.
|
| """
|
| )
|
|
|
|
|
| with gr.Tabs():
|
| with gr.Tab("🎨 Generate"):
|
| with gr.Row():
|
| with gr.Column(scale=1):
|
|
|
| txt_input = gr.Textbox(
|
| label="Historical Subject",
|
| placeholder="Describe a historical scene, artifact, or character...",
|
| lines=3,
|
| info="Be specific! Mention time periods, cultures, or contexts for best results."
|
| )
|
|
|
| btn_generate = gr.Button("🔮 Generate Archive", variant="primary", size="lg")
|
|
|
|
|
| gr.Examples(
|
| examples=[
|
| ["A Roman gladiator training in the Colosseum at sunset"],
|
| ["Medieval blacksmith forging a sword, sparks flying"],
|
| ["Viking longship sailing through a stormy Nordic sea"],
|
| ["Renaissance artist's studio with easels and oil paintings"],
|
| ["Ancient Egyptian scribe writing on papyrus scrolls"],
|
| ["Samurai warrior in ceremonial armor during cherry blossom season"]
|
| ],
|
| inputs=txt_input,
|
| label="💡 Example Prompts (click to use)"
|
| )
|
|
|
| with gr.Column(scale=1):
|
|
|
| output_image = gr.Image(
|
| label="🖼️ Recovered Artifact",
|
| show_label=True
|
| )
|
|
|
|
|
| btn_generate.click(
|
| fn=ui_wrapper,
|
| inputs=[txt_input],
|
| outputs=output_image,
|
| show_progress="full"
|
| )
|
|
|
| with gr.Tab("📚 The Archive"):
|
| gr.Markdown(
|
| """
|
| ### Search the Visual Memory
|
| Use semantic search to find archived artifacts by keywords, themes, or cultural markers.
|
| """
|
| )
|
|
|
| with gr.Row():
|
| search_box = gr.Textbox(
|
| label="Search Query",
|
| placeholder="Try: 'roman armor', 'medieval', 'warrior', or 'architecture'",
|
| scale=4
|
| )
|
| btn_search = gr.Button("🔍 Search", variant="secondary", scale=1)
|
|
|
|
|
|
|
| gallery = gr.Gallery(
|
| label="Found Artifacts",
|
| columns=[1, 2, 3],
|
| height="auto",
|
| object_fit="contain",
|
| show_label=True
|
| )
|
|
|
|
|
| btn_search.click(
|
| fn=search_wrapper,
|
| inputs=[search_box],
|
| outputs=gallery
|
| )
|
|
|
|
|
| gr.Examples(
|
| examples=[
|
| ["roman gladiator"],
|
| ["medieval castle"],
|
| ["viking warrior"],
|
| ["renaissance art"]
|
| ],
|
| inputs=search_box,
|
| label="💡 Quick Search Terms"
|
| )
|
|
|
| if __name__ == "__main__":
|
| app.launch(
|
| server_port=7861,
|
| theme=gr.themes.Monochrome(),
|
| css=custom_css
|
| )
|
|
|