File size: 8,672 Bytes
1f38061
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import gradio as gr
import pandas as pd
import asyncio
import logging
import sys
import os
import io

# 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.")

logger.info("Creating matching agent...")
agent = create_matching_agent(log_manager=log_manager)

# --- 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 {
        log_panel: gr.update(visible=True, open=True),
        log_output: "Starting matching process...\n",
        matching_results_out: "πŸš€ Setting up the matching process..."
    }

    # 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 {
            log_panel: gr.update(),
            log_output: log_stream.getvalue(),
            matching_results_out: "Cannot run matching with fewer than 2 participants."
        }
        return

    logger.info(f"Running matching for {len(participants_df)} participants.")
    progress(0.2, desc="🧠 Agent is thinking...")
    
    # 3. Create a background task for the core matching logic
    match_task = asyncio.create_task(
        run_matching(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 {
            log_panel: gr.update(),
            log_output: log_stream.getvalue(),
            matching_results_out: gr.update()  # 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 {
            log_panel: gr.update(),
            log_output: log_stream.getvalue(),
            matching_results_out: final_report
        }
    except Exception as e:
        logger.error(f"An error occurred during the matching process: {e}", exc_info=True)
        yield {
            log_panel: gr.update(),
            log_output: log_stream.getvalue(),
            matching_results_out: f"An error occurred: {e}"
        }

# --- 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,
                )

    gr.Markdown("<p align='center'>Powered by Tiny Agent</p>")

    # --- 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=[matching_results_out, log_output, log_panel]
    )

# --- 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:
        # Clean up agent resources
        logger.info("Closing agent resources...")
        # We need to run the async close method
        asyncio.run(agent.close())
        logger.info("Cleanup complete.")