Spaces:
Sleeping
Sleeping
| """ | |
| Character Portrait Tab for D'n'D Campaign Manager | |
| """ | |
| import gradio as gr | |
| from typing import Optional, Tuple | |
| from src.agents.character_agent import CharacterAgent | |
| from src.ui.components.dropdown_manager import DropdownManager | |
| class CharacterPortraitTab: | |
| """Generate Portrait tab for AI character portrait generation""" | |
| def __init__(self, character_agent: CharacterAgent, dropdown_manager: DropdownManager): | |
| self.character_agent = character_agent | |
| self.dropdown_manager = dropdown_manager | |
| def generate_portrait_ui( | |
| self, | |
| character_id: str, | |
| style: str = "fantasy art", | |
| quality: str = "standard", | |
| provider: str = "auto" | |
| ) -> Tuple[Optional[str], str]: | |
| """ | |
| Generate character portrait | |
| Returns: | |
| Tuple of (image_path, status_message) | |
| """ | |
| try: | |
| if not character_id.strip(): | |
| return None, "β Error: Please provide a character ID" | |
| # Load character | |
| character = self.character_agent.load_character(character_id) | |
| if not character: | |
| return None, f"β Character not found: {character_id}" | |
| # Generate portrait | |
| file_path, status = self.character_agent.generate_portrait( | |
| character=character, | |
| style=style, | |
| quality=quality, | |
| provider=provider | |
| ) | |
| return file_path, status | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"β Error generating portrait:\n\n{str(e)}\n\n{traceback.format_exc()}" | |
| return None, error_msg | |
| def create(self) -> gr.Dropdown: | |
| """Create and return the Generate Portrait tab component""" | |
| with gr.Tab("Generate Portrait"): | |
| gr.Markdown(""" | |
| ### π¨ AI Character Portrait Generator | |
| Generate stunning character portraits using DALL-E 3 or HuggingFace! | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| portrait_refresh_btn = gr.Button("π Refresh Character List", variant="secondary") | |
| portrait_character_dropdown = gr.Dropdown( | |
| choices=[], | |
| label="Select Character", | |
| info="Choose a character to generate portrait for (type to search)", | |
| allow_custom_value=False, | |
| interactive=True | |
| ) | |
| portrait_provider = gr.Radio( | |
| choices=["auto", "openai", "huggingface"], | |
| label="Image Provider", | |
| value="auto", | |
| info="Auto: Try OpenAI first, fallback to HuggingFace if needed" | |
| ) | |
| portrait_style = gr.Dropdown( | |
| choices=[ | |
| "fantasy art", | |
| "digital painting", | |
| "anime style", | |
| "oil painting", | |
| "watercolor", | |
| "comic book art", | |
| "concept art" | |
| ], | |
| label="Art Style", | |
| value="fantasy art", | |
| info="Choose the artistic style" | |
| ) | |
| portrait_quality = gr.Radio( | |
| choices=["standard", "hd"], | |
| label="Image Quality (OpenAI only)", | |
| value="standard", | |
| info="HD costs more tokens (OpenAI only)" | |
| ) | |
| generate_portrait_btn = gr.Button( | |
| "π¨ Generate Portrait", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| portrait_status = gr.Textbox( | |
| label="Status", | |
| lines=4 | |
| ) | |
| with gr.Column(): | |
| portrait_output = gr.Image( | |
| label="Generated Portrait", | |
| type="filepath", | |
| height=512 | |
| ) | |
| gr.Markdown(""" | |
| **Providers:** | |
| - **OpenAI DALL-E 3**: High quality, costs $0.04/image (standard) or $0.08/image (HD) | |
| - **HuggingFace (Free!)**: Stable Diffusion XL, ~100 requests/day on free tier | |
| - **Auto**: Tries OpenAI first, automatically falls back to HuggingFace if billing issues | |
| Portraits are automatically saved to `data/portraits/` directory. | |
| **Tips:** | |
| - Use "auto" mode for seamless fallback | |
| - OpenAI HD quality produces better results but costs 2x | |
| - HuggingFace is free but may have a 30-60s warm-up time | |
| - Different styles work better for different races/classes | |
| """) | |
| # Refresh portrait character dropdown | |
| portrait_refresh_btn.click( | |
| fn=self.dropdown_manager.refresh_character_dropdown, | |
| inputs=[], | |
| outputs=[portrait_character_dropdown] | |
| ) | |
| # Generate portrait action - convert dropdown label to ID | |
| def generate_portrait_from_dropdown(label, style, quality, provider): | |
| char_id = self.dropdown_manager.get_character_id_from_label(label) | |
| return self.generate_portrait_ui(char_id, style, quality, provider) | |
| generate_portrait_btn.click( | |
| fn=generate_portrait_from_dropdown, | |
| inputs=[portrait_character_dropdown, portrait_style, portrait_quality, portrait_provider], | |
| outputs=[portrait_output, portrait_status] | |
| ) | |
| # Return the dropdown for auto-population | |
| return portrait_character_dropdown | |