Spaces:
Running
Running
| import gradio as gr | |
| import pandas as pd | |
| import asyncio | |
| import logging | |
| import sys | |
| import os | |
| import io | |
| from contextlib import asynccontextmanager | |
| # Change from absolute imports to relative imports | |
| from database import initialize_database, add_participant, get_participants_dataframe | |
| from matching_agent import create_matching_agent, run_matching | |
| from tinyagent.hooks.logging_manager import LoggingManager | |
| from tinyagent.hooks.logging_manager import LoggingManager | |
| import logging | |
| # --- Logging Setup --- | |
| log_manager = LoggingManager(default_level=logging.INFO) | |
| log_manager.set_levels({ | |
| 'tinyagent.hooks.gradio_callback': logging.DEBUG, | |
| 'tinyagent.tiny_agent': logging.DEBUG, | |
| 'tinyagent.mcp_client': logging.DEBUG, | |
| 'tinyagent.code_agent': logging.DEBUG, | |
| }) | |
| console_handler = logging.StreamHandler(sys.stdout) | |
| log_manager.configure_handler( | |
| console_handler, | |
| format_string='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
| level=logging.DEBUG | |
| ) | |
| # Gradio log handler | |
| log_stream = io.StringIO() | |
| gradio_handler = logging.StreamHandler(log_stream) | |
| log_manager.configure_handler( | |
| gradio_handler, | |
| format_string='%(asctime)s - %(levelname)s - %(name)s - %(message)s', | |
| level=logging.DEBUG # Capture detailed logs for the UI | |
| ) | |
| logger = log_manager.get_logger('app') | |
| # --- Initial Setup --- | |
| logger.info("Initializing database...") | |
| initialize_database() | |
| # Check for API key | |
| if not os.environ.get("OPENAI_API_KEY"): | |
| raise ValueError("The OPENAI_API_KEY environment variable is not set. Please set it before running the app.") | |
| # --- Session Management --- | |
| async def agent_session(): | |
| """Create and manage a session-specific agent.""" | |
| session_agent = create_matching_agent(log_manager=log_manager) | |
| try: | |
| yield session_agent | |
| finally: | |
| await session_agent.close() | |
| logger.info("Session agent resources cleaned up") | |
| # --- Gradio UI Functions --- | |
| def register_participant(name, email, linkedin, background, goals): | |
| """Callback function to register a new participant.""" | |
| if not all([name, email]): | |
| return "Please provide at least a name and email.", get_participants_dataframe() | |
| participant_data = { | |
| "name": name, | |
| "email": email, | |
| "linkedin_profile": linkedin, | |
| "background": background, | |
| "goals": goals | |
| } | |
| try: | |
| add_participant(participant_data) | |
| feedback = f"β Success! Participant '{name}' registered." | |
| logger.info(f"Registered new participant: {email}") | |
| except Exception as e: | |
| feedback = f"β Error! Could not register participant. Reason: {e}" | |
| logger.error(f"Failed to register participant {email}: {e}") | |
| return feedback, get_participants_dataframe() | |
| def refresh_participants_list(): | |
| """Callback to reload the participant data from the database.""" | |
| return get_participants_dataframe() | |
| async def run_matching_process(organizer_criteria, progress=gr.Progress(track_tqdm=True)): | |
| """Async callback to run the team matching process, with live log streaming.""" | |
| # 1. Clear previous logs and show panel | |
| log_stream.seek(0) | |
| log_stream.truncate(0) | |
| progress(0, desc="Initializing...") | |
| yield [ | |
| gr.update(visible=True, open=True), # log_panel | |
| "Starting matching process...\n", # log_output | |
| "π Setting up the matching process..." # matching_results_out | |
| ] | |
| # 2. Get participants and perform initial checks | |
| logger.info("Fetching participants...") | |
| progress(0, desc="Fetching participants...") | |
| participants_df = get_participants_dataframe() | |
| if len(participants_df) < 2: | |
| warning_msg = "Matching process aborted: not enough participants." | |
| logger.warning(warning_msg) | |
| yield [ | |
| gr.update(), # log_panel | |
| log_stream.getvalue(), # log_output | |
| "Cannot run matching with fewer than 2 participants." # matching_results_out | |
| ] | |
| return | |
| logger.info(f"Running matching for {len(participants_df)} participants.") | |
| progress(0.2, desc="π§ Agent is thinking...") | |
| # 3. Create a session-specific agent and run matching | |
| async with agent_session() as session_agent: | |
| logger.info("Created session-specific agent") | |
| # Create a background task for the core matching logic | |
| match_task = asyncio.create_task( | |
| run_matching(session_agent, participants_df, organizer_criteria) | |
| ) | |
| # 4. Stream logs while the agent works | |
| while not match_task.done(): | |
| await asyncio.sleep(0.5) # Poll for new logs every 0.5s | |
| yield [ | |
| gr.update(), # log_panel | |
| log_stream.getvalue(), # log_output | |
| gr.update() # matching_results_out - No change to final output yet | |
| ] | |
| # 5. Process the final result from the agent | |
| try: | |
| final_report = await match_task | |
| logger.info("Matching process completed successfully.") | |
| progress(1.0, desc="β Done!") | |
| yield [ | |
| gr.update(), # log_panel | |
| log_stream.getvalue(), # log_output | |
| final_report # matching_results_out | |
| ] | |
| except Exception as e: | |
| logger.error(f"An error occurred during the matching process: {e}", exc_info=True) | |
| yield [ | |
| gr.update(), # log_panel | |
| log_stream.getvalue(), # log_output | |
| f"An error occurred: {e}" # matching_results_out | |
| ] | |
| # --- Gradio App Definition --- | |
| with gr.Blocks(theme=gr.themes.Default( | |
| font=[gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"] | |
| ), title="HackBuddyAI") as app: | |
| gr.Markdown("# π€ HackBuddyAI") | |
| with gr.Tabs(): | |
| with gr.TabItem("π€ Participant Registration"): | |
| gr.Markdown("## Welcome, Participant!") | |
| gr.Markdown("Fill out the form below to register for the hackathon.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| name_in = gr.Textbox(label="Full Name") | |
| email_in = gr.Textbox(label="Email Address") | |
| linkedin_in = gr.Textbox(label="LinkedIn Profile URL", placeholder="Optional") | |
| with gr.Column(): | |
| background_in = gr.Textbox(label="Your Background & Skills", lines=5, placeholder="e.g., Python developer with 3 years of experience, specializing in Django and REST APIs...") | |
| goals_in = gr.Textbox(label="Your Goals for this Hackathon", lines=5, placeholder="e.g., I want to learn about machine learning and work on a cool data visualization project...") | |
| submit_button = gr.Button("Register", variant="primary") | |
| registration_feedback = gr.Markdown() | |
| with gr.TabItem("π Organizer Dashboard"): | |
| gr.Markdown("## Welcome, Organizer!") | |
| gr.Markdown("Here you can view registered participants and run the AI-powered team matching process.") | |
| with gr.Accordion("View Registered Participants", open=False): | |
| refresh_button = gr.Button("π Refresh List") | |
| participants_df_out = gr.DataFrame(value=get_participants_dataframe, interactive=False) | |
| gr.Markdown("### Run Matching") | |
| organizer_criteria_in = gr.Textbox( | |
| label="Matching Criteria", | |
| lines=4, | |
| value="Create teams of 3. Try to balance skills in each team (e.g., frontend, backend, data).", | |
| placeholder="Describe your ideal team composition..." | |
| ) | |
| run_button = gr.Button("π Run AI Matching", variant="primary") | |
| gr.Markdown("### π€ Matched Teams") | |
| matching_results_out = gr.Markdown("Matching has not been run yet.") | |
| with gr.Accordion("Agent Logs", open=False, visible=False) as log_panel: | |
| log_output = gr.Code( | |
| label="Live Logs", | |
| lines=15, | |
| interactive=False, | |
| ) | |
| # Footer | |
| gr.Markdown( | |
| "<div style='text-align: center; margin-top: 20px;'>" | |
| "Built with β€οΈ by <a href='https://github.com/askbudi/tinyagent' target='_blank'>TinyAgent</a>" | |
| "<br>Start building your own AI agents with TinyAgent" | |
| "</div>" | |
| ) | |
| # --- Event Handlers --- | |
| submit_button.click( | |
| fn=register_participant, | |
| inputs=[name_in, email_in, linkedin_in, background_in, goals_in], | |
| outputs=[registration_feedback, participants_df_out] | |
| ) | |
| refresh_button.click( | |
| fn=refresh_participants_list, | |
| inputs=[], | |
| outputs=[participants_df_out] | |
| ) | |
| run_button.click( | |
| fn=run_matching_process, | |
| inputs=[organizer_criteria_in], | |
| outputs=[log_panel, log_output, matching_results_out] | |
| ) | |
| # --- Launching the App --- | |
| if __name__ == "__main__": | |
| try: | |
| logger.info("Launching Gradio app...") | |
| # queue() is important for handling multiple users and async calls | |
| app.queue().launch(share=False) | |
| except KeyboardInterrupt: | |
| logger.info("Gradio app shutting down.") | |
| finally: | |
| logger.info("Shutdown complete.") |