Spaces:
Running
Running
| import os | |
| import sys | |
| import subprocess | |
| from pathlib import Path | |
| import gradio as gr | |
| # Clear proxy settings to avoid connection issues | |
| os.environ.pop('http_proxy', None) | |
| os.environ.pop('https_proxy', None) | |
| os.environ.pop('HTTP_PROXY', None) | |
| os.environ.pop('HTTPS_PROXY', None) | |
| # Add code directory to path | |
| current_dir = Path(__file__).parent | |
| # Use the improved generation method from generate_user_profile_final | |
| code_dir = current_dir / "generate_user_profile_final" / "code" | |
| sys.path.insert(0, str(code_dir)) | |
| # Import necessary modules | |
| from web_api_bridge import generate_profile_from_input | |
| def check_api_key(): | |
| """Check if API key is set""" | |
| api_key = os.environ.get("OPENAI_API_KEY") | |
| if not api_key: | |
| return False | |
| return True | |
| def generate_persona(age, gender, occupation, city, country, custom_values, custom_life_attitude, life_story, interests_hobbies, attribute_count): | |
| """Generate persona""" | |
| if not check_api_key(): | |
| return "β Error: OpenAI API key not set. Please add OPENAI_API_KEY in Hugging Face Space settings." | |
| # Build input data | |
| # Build location dict - only include if both city and country are provided | |
| location_dict = {} | |
| if city and city.strip() and country and country.strip(): | |
| location_dict = { | |
| "city": city.strip(), | |
| "country": country.strip() | |
| } | |
| input_data = { | |
| "basic_info": { | |
| "age": int(age) if age else None, | |
| "gender": gender.strip() if gender and gender.strip() else None, | |
| "occupation": {"status": occupation.strip()} if occupation and occupation.strip() else {}, | |
| "location": location_dict | |
| }, | |
| "custom_values": { | |
| "personal_values": custom_values.strip() if custom_values and custom_values.strip() else None, | |
| "life_attitude": custom_life_attitude.strip() if custom_life_attitude and custom_life_attitude.strip() else None, | |
| "life_story": life_story.strip() if life_story and life_story.strip() else None, | |
| "interests_hobbies": interests_hobbies.strip() if interests_hobbies and interests_hobbies.strip() else None | |
| } | |
| } | |
| # Generate persona with specified attribute count | |
| try: | |
| profile = generate_profile_from_input(input_data, attribute_count=int(attribute_count)) | |
| if "Summary" in profile: | |
| return profile["Summary"] | |
| else: | |
| return "β Error generating persona, could not get summary." | |
| except Exception as e: | |
| import traceback | |
| return f"β Error generating persona: {str(e)}\n{traceback.format_exc()}" | |
| # Custom CSS for better styling | |
| custom_css = """ | |
| body, html { | |
| background: white !important; | |
| min-height: 100vh; | |
| margin: 0 !important; | |
| padding: 0 !important; | |
| } | |
| /* Force all text to be dark and readable */ | |
| body, p, span, div, label, input, textarea, select { | |
| color: #222 !important; | |
| } | |
| /* Exception: keep white text on colored backgrounds */ | |
| .section-header, .section-header * { | |
| color: white !important; | |
| } | |
| .generate-btn, .generate-btn * { | |
| color: white !important; | |
| } | |
| .warning-box, .warning-box * { | |
| color: #856404 !important; | |
| } | |
| .info-box, .info-box * { | |
| color: #004085 !important; | |
| } | |
| .gradio-container { | |
| width: 100% !important; | |
| max-width: none !important; | |
| margin: 0 auto !important; | |
| background: transparent !important; | |
| padding: 0 20px !important; | |
| } | |
| .main { | |
| background: transparent !important; | |
| } | |
| /* Force all backgrounds to be light */ | |
| div, section, form, .app, .contain, .wrap { | |
| background: transparent !important; | |
| } | |
| /* Override Gradio's default dark theme */ | |
| .dark, [data-theme="dark"], .gradio-container.dark { | |
| background: transparent !important; | |
| } | |
| /* Force light background on root elements */ | |
| #root, .app, body > div { | |
| background: white !important; | |
| } | |
| /* Only keep white backgrounds for main columns */ | |
| .input-column, .output-column { | |
| background: rgba(255, 255, 255, 0.95) !important; | |
| } | |
| .header-text { | |
| text-align: center; | |
| color: #222 !important; | |
| font-size: 3.2em !important; | |
| font-weight: bold; | |
| margin-bottom: 0.2em; | |
| } | |
| .subtitle-text { | |
| text-align: center; | |
| color: #333; | |
| font-size: 1.5em; | |
| margin-bottom: 0.8em; | |
| } | |
| .section-header { | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); | |
| color: white !important; | |
| padding: 12px 20px; | |
| border-radius: 8px; | |
| margin: 0 0 12px 0; | |
| font-weight: 700; | |
| font-size: 1.5em; | |
| box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); | |
| text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); | |
| } | |
| /* h3 elements */ | |
| h3 { | |
| font-size: 1.8em !important; | |
| color: #333 !important; | |
| font-weight: 700 !important; | |
| } | |
| /* Ensure section headers inside h3 are visible */ | |
| h3 .section-header { | |
| display: inline-block; | |
| width: 100%; | |
| } | |
| .input-column { | |
| background: #f8f9fa; | |
| padding: 20px; | |
| border-radius: 12px; | |
| box-shadow: 0 2px 12px rgba(0,0,0,0.06); | |
| border: 1px solid #e8e8e8; | |
| } | |
| .output-column { | |
| background: #f8f9fa; | |
| padding: 20px; | |
| border-radius: 12px; | |
| box-shadow: 0 2px 12px rgba(0,0,0,0.06); | |
| border: 1px solid #e8e8e8; | |
| } | |
| .generate-btn { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| border: none !important; | |
| color: white !important; | |
| font-size: 1.6em !important; | |
| font-weight: 600 !important; | |
| padding: 10px 20px !important; | |
| border-radius: 8px !important; | |
| margin-top: 8px !important; | |
| transition: transform 0.2s !important; | |
| width: 100% !important; | |
| } | |
| .generate-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4) !important; | |
| } | |
| .warning-box { | |
| background: rgba(255, 243, 205, 0.9); | |
| border-left: 4px solid #ffc107; | |
| padding: 8px; | |
| border-radius: 8px; | |
| margin: 8px 0; | |
| font-size: 1.2em; | |
| color: #856404; | |
| } | |
| .info-box { | |
| background: rgba(231, 243, 255, 0.9); | |
| border-left: 4px solid #2196F3; | |
| padding: 8px; | |
| border-radius: 8px; | |
| margin: 8px 0; | |
| font-size: 1.2em; | |
| color: #004085; | |
| } | |
| /* Adjust input field spacing */ | |
| .input-column label { | |
| margin-bottom: 4px !important; | |
| margin-top: 8px !important; | |
| font-size: 1.4em !important; | |
| color: #222 !important; | |
| font-weight: 600 !important; | |
| display: block !important; | |
| padding-top: 0px !important; | |
| } | |
| .input-column input, | |
| .input-column textarea, | |
| .input-column select { | |
| font-size: 1.0em !important; | |
| background: white !important; | |
| color: #333 !important; | |
| border: 1px solid #d0d0d0 !important; | |
| border-radius: 6px !important; | |
| padding: 8px 12px !important; | |
| margin-bottom: 0px !important; | |
| } | |
| /* Style for number inputs and dropdowns */ | |
| input[type="number"], | |
| select { | |
| background: white !important; | |
| color: #333 !important; | |
| border: 1px solid #d0d0d0 !important; | |
| font-size: 1.2em !important; | |
| } | |
| /* Remove special styling from dropdown */ | |
| .input-column select, | |
| .input-column .dropdown { | |
| border: 1px solid #e0e0e0 !important; | |
| box-shadow: none !important; | |
| } | |
| /* Dropdown container */ | |
| .input-column div[class*="dropdown"] { | |
| border: none !important; | |
| } | |
| /* Output text area */ | |
| .output-column textarea { | |
| background: white !important; | |
| color: #333 !important; | |
| border: 1px solid #d0d0d0 !important; | |
| border-radius: 6px !important; | |
| padding: 10px !important; | |
| margin-top: 0px !important; | |
| font-size: 1.1em !important; | |
| } | |
| /* Remove spacing in output column */ | |
| .output-column .block, | |
| .output-column .gr-form > div, | |
| .output-column [class*="field"] { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| } | |
| /* Output column label */ | |
| .output-column label { | |
| font-size: 1.2em !important; | |
| margin-bottom: 0px !important; | |
| } | |
| /* Placeholder text */ | |
| ::placeholder { | |
| color: #999 !important; | |
| } | |
| /* Remove ALL dark backgrounds from Gradio containers */ | |
| .input-column .block, | |
| .input-column .form, | |
| .input-column > div, | |
| .input-column div[class*="block"], | |
| .input-column div[class*="form"], | |
| .output-column .block, | |
| .output-column .form, | |
| .output-column > div, | |
| .output-column div[class*="block"], | |
| .output-column div[class*="form"] { | |
| background: transparent !important; | |
| border: none !important; | |
| box-shadow: none !important; | |
| } | |
| /* Target ALL row containers */ | |
| div[class*="gr-row"], | |
| .gr-row, | |
| .input-column .gr-row, | |
| .output-column .gr-row { | |
| background: transparent !important; | |
| border: none !important; | |
| padding: 0 !important; | |
| gap: 12px !important; | |
| margin: 0 !important; | |
| align-items: flex-start !important; | |
| } | |
| /* Ensure columns in rows align properly */ | |
| .input-column .gr-row > div { | |
| flex: 1 1 0 !important; | |
| min-width: 0 !important; | |
| } | |
| /* Remove padding from form containers */ | |
| div[class*="gr-form"], | |
| .gr-form, | |
| .input-column .gr-form { | |
| background: transparent !important; | |
| gap: 0px !important; | |
| margin: 0 !important; | |
| } | |
| /* Style for individual input containers */ | |
| div[class*="gr-box"], | |
| .gr-box, | |
| .input-column .gr-box { | |
| background: transparent !important; | |
| border: none !important; | |
| margin: 0 !important; | |
| padding: 0 !important; | |
| } | |
| /* Reduce padding in all Gradio field containers */ | |
| .input-column .gr-form > div, | |
| .input-column [class*="field"], | |
| .input-column .block { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| } | |
| /* Target specific Gradio component wrappers */ | |
| .input-column .wrap, | |
| .input-column .contain, | |
| .output-column .wrap, | |
| .output-column .contain { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| gap: 0 !important; | |
| } | |
| /* Minimize spacing between form fields */ | |
| .input-column > div > div > div { | |
| margin: 0 !important; | |
| padding: 0 !important; | |
| } | |
| /* Remove extra spacing from Gradio's internal divs */ | |
| .input-column div[data-testid], | |
| .input-column div[class*="svelte"] { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| } | |
| /* Column spacing in rows */ | |
| .input-column .gr-row > div { | |
| margin: 0 !important; | |
| padding: 0 2px !important; | |
| flex: 1 1 0 !important; | |
| min-width: 0 !important; | |
| } | |
| /* Ensure equal width for inputs in the same row */ | |
| .input-column .gr-row > div > div { | |
| width: 100% !important; | |
| } | |
| /* Make all inputs in rows have equal height */ | |
| .input-column .gr-row input, | |
| .input-column .gr-row select, | |
| .input-column .gr-row textarea { | |
| height: 42px !important; | |
| min-height: 42px !important; | |
| } | |
| /* Target container divs more aggressively */ | |
| .input-column > div > div, | |
| .output-column > div > div { | |
| background: transparent !important; | |
| } | |
| /* Specific fix for dropdown and number input containers */ | |
| .input-column label + div, | |
| .output-column label + div { | |
| background: transparent !important; | |
| border: none !important; | |
| } | |
| """ | |
| # Create Gradio interface with modern design | |
| with gr.Blocks( | |
| css=custom_css, | |
| title="DeepPersona - AI Character Generator", | |
| theme=gr.themes.Default(primary_hue="purple", secondary_hue="blue") | |
| ) as demo: | |
| # Header | |
| gr.Markdown("<h1 class='header-text'>π DeepPersona</h1>") | |
| gr.Markdown("<p class='subtitle-text'>Generate Realistic AI Characters with Rich Personalities</p>") | |
| # Main Content | |
| with gr.Row(equal_height=True): | |
| # Left Column - Inputs | |
| with gr.Column(scale=2, elem_classes="input-column"): | |
| gr.Markdown("<h3 class='section-header'>π€ Basic Information</h3>") | |
| with gr.Row(): | |
| age = gr.Number( | |
| label="Age", | |
| minimum=1, | |
| maximum=120, | |
| placeholder="25" | |
| ) | |
| gender = gr.Textbox( | |
| label="Gender", | |
| placeholder="Male, Female..." | |
| ) | |
| occupation = gr.Textbox( | |
| label="Occupation", | |
| placeholder="Software Engineer, Teacher, Artist..." | |
| ) | |
| with gr.Row(): | |
| city = gr.Textbox( | |
| label="City", | |
| placeholder="Mumbai, New York..." | |
| ) | |
| country = gr.Textbox( | |
| label="Country", | |
| placeholder="India, USA..." | |
| ) | |
| gr.Markdown("<h3 class='section-header'>β¨ Custom Values (Optional)</h3>") | |
| custom_values = gr.Textbox( | |
| label="Personal Values", | |
| lines=2, | |
| placeholder="Values family, creativity, continuous learning..." | |
| ) | |
| custom_life_attitude = gr.Textbox( | |
| label="Life Attitude", | |
| lines=2, | |
| placeholder="Optimistic and solution-oriented..." | |
| ) | |
| life_story = gr.Textbox( | |
| label="Life Story", | |
| lines=2, | |
| placeholder="Born in a small town, moved to the city for education..." | |
| ) | |
| interests_hobbies = gr.Textbox( | |
| label="Interests and Hobbies", | |
| lines=2, | |
| placeholder="Reading, hiking, photography, cooking..." | |
| ) | |
| gr.Markdown("<h3 class='section-header'>βοΈ Advanced Settings</h3>") | |
| attribute_count = gr.Slider( | |
| minimum=100, | |
| maximum=350, | |
| value=200, | |
| step=50, | |
| label="Attribute Richness" | |
| ) | |
| # Right Column - Output | |
| with gr.Column(scale=3, elem_classes="output-column"): | |
| gr.Markdown("<h3 class='section-header'>π Generated Character Profile</h3>") | |
| output = gr.Textbox( | |
| label="", | |
| lines=30, | |
| placeholder="Your generated character profile will appear here...\n\nClick 'Generate Character Profile' to start!", | |
| show_label=False, | |
| container=False | |
| ) | |
| # Generate Button - Full Width | |
| generate_btn = gr.Button( | |
| "π Generate Character Profile", | |
| variant="primary", | |
| size="lg", | |
| elem_classes="generate-btn" | |
| ) | |
| # Footer | |
| gr.Markdown( | |
| """ | |
| <div style='text-align: center; margin-top: 30px; padding: 20px; background: rgba(255, 255, 255, 0.9); border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'> | |
| <p style='color: #555; margin: 5px 0;'> | |
| π <a href='https://thzva.github.io/deeppersona.github.io/' target='_blank' style='color: #667eea; text-decoration: none; font-weight: 500;'>Project Homepage</a> | | |
| π <a href='https://huggingface.co/datasets/THzva/deeppersona_dataset' target='_blank' style='color: #667eea; text-decoration: none; font-weight: 500;'>Dataset</a> | | |
| π» <a href='https://github.com/thzva/Deeppersona' target='_blank' style='color: #667eea; text-decoration: none; font-weight: 500;'>GitHub</a> | |
| </p> | |
| <p style='color: #888; font-size: 0.9em; margin-top: 10px;'> | |
| Powered by GPT-4.1-mini β’ Built with β€οΈ by DeepPersona Team | |
| </p> | |
| </div> | |
| """ | |
| ) | |
| # Event Handler | |
| generate_btn.click( | |
| fn=generate_persona, | |
| inputs=[age, gender, occupation, city, country, custom_values, custom_life_attitude, life_story, interests_hobbies, attribute_count], | |
| outputs=output | |
| ) | |
| # Launch application | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |