Mahdiyar commited on
Commit
9a255bf
Β·
1 Parent(s): 1f38061

initial version

Browse files
README.md CHANGED
@@ -11,4 +11,98 @@ license: apache-2.0
11
  short_description: Instantly match hackathon participants into ideal teams!
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  short_description: Instantly match hackathon participants into ideal teams!
12
  ---
13
 
14
+ # πŸ€– AI-Powered HackBuddyAI
15
+
16
+ This project is a web application that helps hackathon organizers automatically form balanced teams from a list of participants. It uses a `TinyCodeAgent` to analyze participant skills and goals, creating optimal teams based on criteria defined by the organizer.
17
+
18
+ The application features a two-tab interface:
19
+ 1. **Participant Registration**: A form for participants to submit their profile, including skills, background, and what they want to achieve during the hackathon.
20
+ 2. **Organizer Dashboard**: A view for the organizer to see all registered participants, define matching criteria, and run the AI-powered team formation process.
21
+
22
+ ## Tech Stack
23
+ - **Backend**: Python
24
+ - **AI Agent Framework**: `TinyCodeAgent`
25
+ - **Web UI**: Gradio
26
+ - **Data Handling**: Pandas
27
+ - **Database**: SQLite
28
+
29
+ ---
30
+
31
+ ## Setup and Installation
32
+
33
+ ### 1. Prerequisites
34
+ - Python 3.8+
35
+ - An OpenAI API key
36
+
37
+ ### 2. Installation
38
+ 1. **Clone the repository** (if applicable) or ensure you have the project files in a directory.
39
+
40
+ 2. **Navigate to the project directory**:
41
+ ```bash
42
+ cd path/to/project
43
+ ```
44
+
45
+ 3. **Create a virtual environment** (recommended):
46
+ ```bash
47
+ python -m venv venv
48
+ source venv/bin/activate # On Windows, use `venv\Scripts\activate`
49
+ ```
50
+
51
+ 4. **Install dependencies**:
52
+ The project relies on the `tinyagent` library. Assuming it is available in your environment, install the other required packages:
53
+ ```bash
54
+ pip install -r hackathon_organizer/requirements.txt
55
+ ```
56
+
57
+ ### 3. Configure Environment Variables
58
+ You must set your OpenAI API key as an environment variable. The application will not run without it.
59
+
60
+ - **On macOS/Linux**:
61
+ ```bash
62
+ export OPENAI_API_KEY="your_api_key_here"
63
+ ```
64
+ - **On Windows (Command Prompt)**:
65
+ ```bash
66
+ set OPENAI_API_KEY="your_api_key_here"
67
+ ```
68
+
69
+ ---
70
+
71
+ ## πŸš€ Running the Application
72
+
73
+ Once the setup is complete, you can launch the Gradio web application using one of the following methods:
74
+
75
+ ### Method 1: Using the main script (recommended)
76
+
77
+ ```bash
78
+ python main.py
79
+ ```
80
+
81
+ This script handles all the necessary path configurations and launches the application.
82
+
83
+ ### Method 2: Running app.py directly
84
+
85
+ ```bash
86
+ cd hackathon_organizer
87
+ python app.py
88
+ ```
89
+
90
+ Either method will start a local web server, and you can access the application at the URL provided in the console (usually `http://127.0.0.1:7860`).
91
+
92
+ The application will create a `hackathon_participants.db` file in the working directory to store the participant data.
93
+
94
+ ---
95
+
96
+ ## πŸ§ͺ Running Tests
97
+
98
+ The project includes unit tests for the database and the agent setup logic. The tests are located in the `tests/` directory.
99
+
100
+ To run the tests:
101
+
102
+ ```bash
103
+ # From the project root directory
104
+ cd hackathon_organizer
105
+ python -m unittest discover tests
106
+ ```
107
+
108
+ The tests are designed to run without needing an active internet connection or a valid API key. They use mocks to simulate agent behavior and an in-memory database for testing database operations.
hackathon_organizer/__init__.py β†’ __init__.py RENAMED
File without changes
hackathon_organizer/app.py β†’ app.py RENAMED
File without changes
app_simple.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import logging
4
+ import sys
5
+ import os
6
+
7
+ from database import initialize_database, add_participant, get_participants_dataframe
8
+
9
+ # --- Logging Setup ---
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
13
+ handlers=[logging.StreamHandler(sys.stdout)]
14
+ )
15
+ logger = logging.getLogger('app_simple')
16
+
17
+ # --- Initial Setup ---
18
+ logger.info("Initializing database...")
19
+ initialize_database()
20
+
21
+ # --- Gradio UI Functions ---
22
+
23
+ def register_participant(name, email, linkedin, background, goals):
24
+ """Callback function to register a new participant."""
25
+ if not all([name, email]):
26
+ return "Please provide at least a name and email.", get_participants_dataframe()
27
+
28
+ participant_data = {
29
+ "name": name,
30
+ "email": email,
31
+ "linkedin_profile": linkedin,
32
+ "background": background,
33
+ "goals": goals
34
+ }
35
+ try:
36
+ add_participant(participant_data)
37
+ feedback = f"βœ… Success! Participant '{name}' registered."
38
+ logger.info(f"Registered new participant: {email}")
39
+ except Exception as e:
40
+ feedback = f"❌ Error! Could not register participant. Reason: {e}"
41
+ logger.error(f"Failed to register participant {email}: {e}")
42
+
43
+ return feedback, get_participants_dataframe()
44
+
45
+ def refresh_participants_list():
46
+ """Callback to reload the participant data from the database."""
47
+ return get_participants_dataframe()
48
+
49
+ def mock_matching_process(organizer_criteria):
50
+ """Mock function for the matching process (without using TinyCodeAgent)."""
51
+ participants_df = get_participants_dataframe()
52
+ if len(participants_df) < 2:
53
+ logger.warning("Matching process aborted: not enough participants.")
54
+ return "Cannot run matching with fewer than 2 participants."
55
+
56
+ # Create a simple mock output
57
+ result = f"""
58
+ ## Team Matching Results
59
+
60
+ **Criteria used**: {organizer_criteria}
61
+
62
+ ### Team 1
63
+ * **{participants_df['name'].iloc[0] if len(participants_df) > 0 else 'No participants'}**
64
+ * **{participants_df['name'].iloc[1] if len(participants_df) > 1 else 'No participants'}**
65
+
66
+ **Justification**: This is a mock team created for demonstration purposes.
67
+
68
+ ### Team 2
69
+ * **{participants_df['name'].iloc[2] if len(participants_df) > 2 else 'No participants'}**
70
+ * **{participants_df['name'].iloc[3] if len(participants_df) > 3 else 'No participants'}**
71
+
72
+ **Justification**: This is another mock team created for demonstration purposes.
73
+
74
+ *Note: This is a simplified version without the AI matching. The full version would use TinyCodeAgent to create optimized teams.*
75
+ """
76
+ return result
77
+
78
+ # --- Gradio App Definition ---
79
+
80
+ with gr.Blocks(theme=gr.themes.Soft(), title="HackBuddyAI (Simple)") as app:
81
+ gr.Markdown("# πŸ€– HackBuddyAI (Simple Version)")
82
+ gr.Markdown("*This is a simplified version without the AI matching component.*")
83
+
84
+ with gr.Tabs():
85
+ with gr.TabItem("πŸ‘€ Participant Registration"):
86
+ gr.Markdown("## Welcome, Participant!")
87
+ gr.Markdown("Fill out the form below to register for the hackathon.")
88
+ with gr.Row():
89
+ with gr.Column():
90
+ name_in = gr.Textbox(label="Full Name")
91
+ email_in = gr.Textbox(label="Email Address")
92
+ linkedin_in = gr.Textbox(label="LinkedIn Profile URL", placeholder="Optional")
93
+ with gr.Column():
94
+ 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...")
95
+ 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...")
96
+
97
+ submit_button = gr.Button("Register", variant="primary")
98
+ registration_feedback = gr.Markdown()
99
+
100
+ with gr.TabItem("πŸ‘‘ Organizer Dashboard"):
101
+ gr.Markdown("## Welcome, Organizer!")
102
+ gr.Markdown("Here you can view registered participants and run the team matching process.")
103
+
104
+ with gr.Accordion("View Registered Participants", open=False):
105
+ refresh_button = gr.Button("πŸ”„ Refresh List")
106
+ participants_df_out = gr.DataFrame(value=get_participants_dataframe, interactive=False)
107
+
108
+ gr.Markdown("### Run Matching")
109
+ organizer_criteria_in = gr.Textbox(
110
+ label="Matching Criteria",
111
+ lines=4,
112
+ value="Create teams of 3. Try to balance skills in each team (e.g., frontend, backend, data).",
113
+ placeholder="Describe your ideal team composition..."
114
+ )
115
+ run_button = gr.Button("πŸš€ Run Matching", variant="primary")
116
+
117
+ gr.Markdown("### 🀝 Matched Teams")
118
+ matching_results_out = gr.Markdown("Matching has not been run yet.")
119
+
120
+ # --- Event Handlers ---
121
+ submit_button.click(
122
+ fn=register_participant,
123
+ inputs=[name_in, email_in, linkedin_in, background_in, goals_in],
124
+ outputs=[registration_feedback, participants_df_out]
125
+ )
126
+
127
+ refresh_button.click(
128
+ fn=refresh_participants_list,
129
+ inputs=[],
130
+ outputs=[participants_df_out]
131
+ )
132
+
133
+ run_button.click(
134
+ fn=mock_matching_process,
135
+ inputs=[organizer_criteria_in],
136
+ outputs=[matching_results_out]
137
+ )
138
+
139
+ # --- Launching the App ---
140
+ if __name__ == "__main__":
141
+ try:
142
+ logger.info("Launching Gradio app (simple version)...")
143
+ # queue() is important for handling multiple users
144
+ app.queue().launch(share=False)
145
+ except KeyboardInterrupt:
146
+ logger.info("Gradio app shutting down.")
hackathon_organizer/database.py β†’ database.py RENAMED
File without changes
generate_participants.py DELETED
@@ -1,372 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Generate Synthetic Hackathon Participants
4
-
5
- This script creates 100 diverse hackathon participants and saves them to the database.
6
- The participants represent various personas that might attend a hackathon focused on
7
- "Connected Experiences" and AI agents.
8
- """
9
- import random
10
- import sys
11
- import os
12
- from faker import Faker
13
- from hackathon_organizer.database import initialize_database, add_participant, get_participants_dataframe
14
-
15
- # Initialize Faker for generating realistic names and emails
16
- fake = Faker()
17
-
18
- # Define various participant personas and attributes
19
- TECHNICAL_BACKGROUNDS = [
20
- # Developers
21
- "Full-stack developer with {years} years of experience in {stack}. {additional}",
22
- "Backend engineer specializing in {backend_tech}. {additional}",
23
- "Frontend developer focused on {frontend_tech}. {additional}",
24
- "Mobile developer with expertise in {mobile_tech}. {additional}",
25
- "DevOps engineer with experience in {devops_tech}. {additional}",
26
- "Data scientist working with {ds_tech}. {additional}",
27
- "Machine learning engineer focused on {ml_tech}. {additional}",
28
- "AI researcher specializing in {ai_tech}. {additional}",
29
- "Game developer using {game_tech}. {additional}",
30
- "Embedded systems engineer working with {embedded_tech}. {additional}",
31
-
32
- # Technical but not coding-focused
33
- "UX/UI designer with {years} years of experience. {additional}",
34
- "Product manager for {product_type} products. {additional}",
35
- "QA engineer with expertise in {qa_tech}. {additional}",
36
- "Technical writer specializing in {writing_focus}. {additional}",
37
- "Solution architect with background in {arch_focus}. {additional}",
38
- ]
39
-
40
- NON_TECHNICAL_BACKGROUNDS = [
41
- "Marketing professional with experience in {marketing_focus}. {additional}",
42
- "Business development specialist in the {industry} industry. {additional}",
43
- "Project manager with {years} years of experience in {pm_focus}. {additional}",
44
- "Entrepreneur and founder of a {startup_type} startup. {additional}",
45
- "Student studying {field} at {university}. {additional}",
46
- "Design thinking facilitator and innovation consultant. {additional}",
47
- "Content creator focusing on {content_focus}. {additional}",
48
- "Sales professional in the {sales_industry} sector. {additional}",
49
- "HR specialist interested in tech talent and culture. {additional}",
50
- "Non-profit professional looking to leverage technology for social impact. {additional}",
51
- ]
52
-
53
- GOALS = [
54
- # Learning-focused
55
- "I want to learn about AI and how it can enhance user experiences at events.",
56
- "I'm here to understand how to build AI agents and apply them to real-world problems.",
57
- "I hope to gain practical experience with AI technologies and expand my technical skills.",
58
- "I want to learn from experienced developers and improve my coding abilities.",
59
- "I'm looking to understand how AI can create more meaningful human connections.",
60
-
61
- # Project-focused
62
- "I want to build a prototype that demonstrates the power of AI in connecting people.",
63
- "I'm hoping to create an innovative solution that addresses the challenges of virtual events.",
64
- "My goal is to develop an AI agent that enhances real-world social interactions.",
65
- "I want to build something impressive for my portfolio that showcases my skills.",
66
- "I'm aiming to create a practical tool that event organizers can actually use.",
67
-
68
- # Networking-focused
69
- "I'm primarily here to network with other professionals in the AI and event space.",
70
- "I want to meet potential co-founders for a startup idea I've been developing.",
71
- "I'm looking to connect with mentors who can guide my career in tech.",
72
- "I hope to find collaborators for future projects beyond this hackathon.",
73
- "I want to expand my professional network in the Toronto tech community.",
74
-
75
- # Career-focused
76
- "I'm exploring career opportunities in AI development and looking to showcase my skills.",
77
- "I want to transition from my current role to a more tech-focused position.",
78
- "I'm hoping this experience will help me land a job at an innovative tech company.",
79
- "I want to demonstrate my abilities to potential employers or clients.",
80
- "I'm building skills that will help me advance in my current organization.",
81
-
82
- # Fun/Experience-focused
83
- "I'm here for the creative experience and the thrill of building something in 24 hours.",
84
- "I want to have fun while challenging myself technically.",
85
- "I'm curious about hackathons and wanted to experience one firsthand.",
86
- "I enjoy the collaborative atmosphere of hackathons and the energy they generate.",
87
- "I'm looking for a break from my routine and a chance to work on something different.",
88
- ]
89
-
90
- # Technical stack components
91
- STACK_COMPONENTS = {
92
- "years": [str(i) for i in range(1, 16)],
93
- "stack": [
94
- "JavaScript/TypeScript and Python", "MERN stack", "MEAN stack", "Ruby on Rails",
95
- "Django and React", "Vue.js and Node.js", "PHP and Laravel", "Java Spring Boot",
96
- ".NET and Angular", "Go and React", "Python Flask and Vue.js"
97
- ],
98
- "backend_tech": [
99
- "Node.js and Express", "Django and PostgreSQL", "Ruby on Rails", "Java Spring Boot",
100
- "ASP.NET Core", "PHP and Laravel", "Go microservices", "Python FastAPI",
101
- "GraphQL APIs", "Serverless architectures on AWS"
102
- ],
103
- "frontend_tech": [
104
- "React and Redux", "Angular and RxJS", "Vue.js and Vuex", "Svelte and SvelteKit",
105
- "Next.js", "Gatsby", "React Native", "Flutter", "TypeScript and Material UI",
106
- "Tailwind CSS and Alpine.js"
107
- ],
108
- "mobile_tech": [
109
- "React Native", "Flutter", "Swift for iOS", "Kotlin for Android",
110
- "Xamarin", "Ionic", "PWAs", "Unity for mobile games", "NativeScript",
111
- "Mobile AR/VR applications"
112
- ],
113
- "devops_tech": [
114
- "Kubernetes and Docker", "AWS infrastructure", "Azure DevOps", "Google Cloud Platform",
115
- "CI/CD pipelines", "Terraform and infrastructure as code", "Jenkins and GitLab CI",
116
- "Monitoring and observability tools", "Site Reliability Engineering practices",
117
- "Security automation"
118
- ],
119
- "ds_tech": [
120
- "Python, Pandas, and scikit-learn", "R and Tidyverse", "SQL and data warehousing",
121
- "Tableau and data visualization", "Big data technologies like Spark",
122
- "ETL pipelines", "Statistical analysis", "A/B testing methodologies",
123
- "Natural Language Processing", "Computer Vision"
124
- ],
125
- "ml_tech": [
126
- "TensorFlow and Keras", "PyTorch", "scikit-learn", "deep learning models",
127
- "MLOps and model deployment", "reinforcement learning", "computer vision algorithms",
128
- "NLP models", "recommendation systems", "time series forecasting"
129
- ],
130
- "ai_tech": [
131
- "large language models", "generative AI", "conversational agents", "computer vision systems",
132
- "reinforcement learning", "multimodal AI", "AI ethics and responsible AI",
133
- "autonomous systems", "AI for social good", "explainable AI"
134
- ],
135
- "game_tech": [
136
- "Unity", "Unreal Engine", "Godot", "WebGL", "AR/VR development",
137
- "mobile game development", "game AI", "procedural generation",
138
- "multiplayer networking", "game physics"
139
- ],
140
- "embedded_tech": [
141
- "Arduino", "Raspberry Pi", "IoT devices", "embedded Linux",
142
- "RTOS", "C/C++ for microcontrollers", "sensor networks",
143
- "firmware development", "hardware interfaces", "low-power systems"
144
- ],
145
- "product_type": [
146
- "SaaS", "mobile", "enterprise", "consumer", "AI-powered",
147
- "IoT", "fintech", "healthtech", "edtech", "e-commerce"
148
- ],
149
- "qa_tech": [
150
- "automated testing", "Selenium and Cypress", "performance testing",
151
- "security testing", "mobile app testing", "API testing",
152
- "test-driven development", "behavior-driven development",
153
- "continuous integration testing", "accessibility testing"
154
- ],
155
- "writing_focus": [
156
- "API documentation", "user guides", "developer tutorials",
157
- "knowledge bases", "technical blogs", "software requirements",
158
- "open source documentation", "technical specifications",
159
- "UX writing", "compliance documentation"
160
- ],
161
- "arch_focus": [
162
- "cloud architectures", "microservices", "serverless",
163
- "enterprise systems", "distributed systems", "API design",
164
- "security architectures", "data platforms", "IoT systems",
165
- "mobile and web applications"
166
- ],
167
- "additional": [
168
- "I enjoy working in collaborative environments.",
169
- "I'm passionate about creating accessible technology.",
170
- "I've contributed to several open source projects.",
171
- "I'm interested in ethical technology and responsible innovation.",
172
- "I enjoy mentoring junior developers.",
173
- "I have a background in design thinking.",
174
- "I've worked in startups and enterprise environments.",
175
- "I'm particularly interested in AI ethics.",
176
- "I love solving complex algorithmic problems.",
177
- "I focus on creating user-centered solutions.",
178
- "I have experience leading small technical teams.",
179
- "I'm self-taught and constantly learning new technologies.",
180
- "I have a computer science degree but learned most of my skills on the job.",
181
- "I'm currently transitioning careers into tech.",
182
- "I'm an advocate for diversity in tech.",
183
- "I've organized tech meetups and community events.",
184
- "I'm interested in the intersection of technology and sustainability.",
185
- "I have experience in both technical and business roles.",
186
- "I'm passionate about making technology more accessible to everyone.",
187
- "I enjoy the challenges of working with legacy systems.",
188
- "", # Empty for some participants
189
- ]
190
- }
191
-
192
- # Non-technical components
193
- NON_TECH_COMPONENTS = {
194
- "marketing_focus": [
195
- "digital marketing", "content strategy", "brand development",
196
- "social media campaigns", "event promotion", "growth hacking",
197
- "community building", "influencer partnerships", "SEO/SEM",
198
- "product marketing"
199
- ],
200
- "industry": [
201
- "technology", "healthcare", "finance", "education", "retail",
202
- "entertainment", "manufacturing", "non-profit", "government",
203
- "hospitality"
204
- ],
205
- "years": [str(i) for i in range(1, 16)],
206
- "pm_focus": [
207
- "agile methodologies", "waterfall approaches", "hybrid frameworks",
208
- "technical projects", "creative initiatives", "product launches",
209
- "organizational change", "international teams", "startup environments",
210
- "enterprise transformations"
211
- ],
212
- "startup_type": [
213
- "tech", "social impact", "e-commerce", "healthcare", "education",
214
- "fintech", "sustainability", "B2B SaaS", "consumer app", "AI/ML"
215
- ],
216
- "field": [
217
- "Computer Science", "Business Administration", "Design", "Marketing",
218
- "Engineering", "Data Science", "Psychology", "Communications",
219
- "Information Technology", "Entrepreneurship"
220
- ],
221
- "university": [
222
- "University of Toronto", "York University", "Ryerson University",
223
- "Seneca College", "Humber College", "OCAD University",
224
- "George Brown College", "McMaster University", "Waterloo University",
225
- "Queen's University"
226
- ],
227
- "content_focus": [
228
- "tech tutorials", "industry trends", "career development",
229
- "product reviews", "educational content", "lifestyle and tech",
230
- "startup stories", "coding challenges", "design inspiration",
231
- "thought leadership"
232
- ],
233
- "sales_industry": [
234
- "SaaS", "hardware", "consulting services", "enterprise solutions",
235
- "consumer tech", "B2B technology", "telecommunications",
236
- "cybersecurity", "cloud services", "digital transformation"
237
- ],
238
- "additional": [
239
- "I'm excited to learn more about technology and how it can solve real problems.",
240
- "I bring a unique perspective from my non-technical background.",
241
- "I'm interested in the human aspects of technology.",
242
- "I'm looking to collaborate with technical team members and contribute my skills.",
243
- "I have strong communication and presentation skills.",
244
- "I excel at understanding user needs and translating them into requirements.",
245
- "I'm good at explaining complex concepts to diverse audiences.",
246
- "I have experience managing stakeholder expectations.",
247
- "I'm skilled at identifying market opportunities.",
248
- "I enjoy bridging the gap between technical and non-technical teams.",
249
- "I have a creative approach to problem-solving.",
250
- "I'm passionate about user experience and accessibility.",
251
- "I have a network of industry connections that could be valuable.",
252
- "I'm experienced in gathering and synthesizing user feedback.",
253
- "I'm interested in how technology can create social impact.",
254
- "I have experience in project coordination and team organization.",
255
- "I'm good at creating compelling narratives around technical products.",
256
- "I'm curious about AI and its potential applications.",
257
- "I have a background in psychology and understand human behavior.",
258
- "I'm skilled at facilitating workshops and brainstorming sessions.",
259
- "", # Empty for some participants
260
- ]
261
- }
262
-
263
- def generate_background(is_technical=True):
264
- """Generate a realistic background for a participant."""
265
- if is_technical:
266
- template = random.choice(TECHNICAL_BACKGROUNDS)
267
- components = STACK_COMPONENTS
268
- else:
269
- template = random.choice(NON_TECHNICAL_BACKGROUNDS)
270
- components = NON_TECH_COMPONENTS
271
-
272
- # Fill in the template with random components
273
- for key in components:
274
- if "{" + key + "}" in template:
275
- template = template.replace("{" + key + "}", random.choice(components[key]))
276
-
277
- return template
278
-
279
- def generate_linkedin_profile(name):
280
- """Generate a realistic LinkedIn profile URL based on the name."""
281
- # Remove spaces and special characters, convert to lowercase
282
- name_part = ''.join(c for c in name if c.isalnum()).lower()
283
-
284
- # Add some randomness to ensure uniqueness
285
- if random.random() < 0.3:
286
- # Some people use just their name
287
- profile = name_part
288
- elif random.random() < 0.6:
289
- # Some add a random number
290
- profile = f"{name_part}{random.randint(1, 999)}"
291
- else:
292
- # Some add their profession or location
293
- suffixes = ["dev", "tech", "to", "canada", "design", "pm", "product", "marketing", "ai"]
294
- profile = f"{name_part}-{random.choice(suffixes)}"
295
-
296
- return f"linkedin.com/in/{profile}"
297
-
298
- def generate_participants(count=100):
299
- """Generate a specified number of diverse hackathon participants."""
300
- participants = []
301
-
302
- # Define the distribution of technical vs non-technical participants
303
- # For a hackathon, we'll have more technical participants but still a good mix
304
- technical_count = int(count * 0.7) # 70% technical
305
- non_technical_count = count - technical_count # 30% non-technical
306
-
307
- # Generate technical participants
308
- for _ in range(technical_count):
309
- name = fake.name()
310
- email = fake.email()
311
- linkedin = generate_linkedin_profile(name)
312
- background = generate_background(is_technical=True)
313
- goals = random.choice(GOALS)
314
-
315
- participants.append({
316
- "email": email,
317
- "name": name,
318
- "linkedin_profile": linkedin,
319
- "background": background,
320
- "goals": goals
321
- })
322
-
323
- # Generate non-technical participants
324
- for _ in range(non_technical_count):
325
- name = fake.name()
326
- email = fake.email()
327
- linkedin = generate_linkedin_profile(name)
328
- background = generate_background(is_technical=False)
329
- goals = random.choice(GOALS)
330
-
331
- participants.append({
332
- "email": email,
333
- "name": name,
334
- "linkedin_profile": linkedin,
335
- "background": background,
336
- "goals": goals
337
- })
338
-
339
- # Shuffle the participants to mix technical and non-technical
340
- random.shuffle(participants)
341
- return participants
342
-
343
- def main():
344
- """Main function to generate participants and save them to the database."""
345
- print("Initializing database...")
346
- initialize_database()
347
-
348
- print("Generating 100 diverse hackathon participants...")
349
- participants = generate_participants(100)
350
-
351
- print("Adding participants to the database...")
352
- for p in participants:
353
- add_participant(p)
354
-
355
- print("Participants added successfully.")
356
-
357
- # Get and display a sample of the participants
358
- df = get_participants_dataframe()
359
- print(f"\nTotal participants in database: {len(df)}")
360
- print("\nSample of participants:")
361
- print(df.sample(5))
362
-
363
- if __name__ == "__main__":
364
- # Check if Faker is installed
365
- try:
366
- import faker
367
- except ImportError:
368
- print("The 'faker' package is required but not installed.")
369
- print("Please install it using: pip install faker")
370
- sys.exit(1)
371
-
372
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hackathon_organizer/README.md DELETED
@@ -1,95 +0,0 @@
1
- # πŸ€– AI-Powered HackBuddyAI
2
-
3
- This project is a web application that helps hackathon organizers automatically form balanced teams from a list of participants. It uses a `TinyCodeAgent` to analyze participant skills and goals, creating optimal teams based on criteria defined by the organizer.
4
-
5
- The application features a two-tab interface:
6
- 1. **Participant Registration**: A form for participants to submit their profile, including skills, background, and what they want to achieve during the hackathon.
7
- 2. **Organizer Dashboard**: A view for the organizer to see all registered participants, define matching criteria, and run the AI-powered team formation process.
8
-
9
- ## Tech Stack
10
- - **Backend**: Python
11
- - **AI Agent Framework**: `TinyCodeAgent`
12
- - **Web UI**: Gradio
13
- - **Data Handling**: Pandas
14
- - **Database**: SQLite
15
-
16
- ---
17
-
18
- ## Setup and Installation
19
-
20
- ### 1. Prerequisites
21
- - Python 3.8+
22
- - An OpenAI API key
23
-
24
- ### 2. Installation
25
- 1. **Clone the repository** (if applicable) or ensure you have the project files in a directory.
26
-
27
- 2. **Navigate to the project directory**:
28
- ```bash
29
- cd path/to/project
30
- ```
31
-
32
- 3. **Create a virtual environment** (recommended):
33
- ```bash
34
- python -m venv venv
35
- source venv/bin/activate # On Windows, use `venv\Scripts\activate`
36
- ```
37
-
38
- 4. **Install dependencies**:
39
- The project relies on the `tinyagent` library. Assuming it is available in your environment, install the other required packages:
40
- ```bash
41
- pip install -r hackathon_organizer/requirements.txt
42
- ```
43
-
44
- ### 3. Configure Environment Variables
45
- You must set your OpenAI API key as an environment variable. The application will not run without it.
46
-
47
- - **On macOS/Linux**:
48
- ```bash
49
- export OPENAI_API_KEY="your_api_key_here"
50
- ```
51
- - **On Windows (Command Prompt)**:
52
- ```bash
53
- set OPENAI_API_KEY="your_api_key_here"
54
- ```
55
-
56
- ---
57
-
58
- ## πŸš€ Running the Application
59
-
60
- Once the setup is complete, you can launch the Gradio web application using one of the following methods:
61
-
62
- ### Method 1: Using the main script (recommended)
63
-
64
- ```bash
65
- python main.py
66
- ```
67
-
68
- This script handles all the necessary path configurations and launches the application.
69
-
70
- ### Method 2: Running app.py directly
71
-
72
- ```bash
73
- cd hackathon_organizer
74
- python app.py
75
- ```
76
-
77
- Either method will start a local web server, and you can access the application at the URL provided in the console (usually `http://127.0.0.1:7860`).
78
-
79
- The application will create a `hackathon_participants.db` file in the working directory to store the participant data.
80
-
81
- ---
82
-
83
- ## πŸ§ͺ Running Tests
84
-
85
- The project includes unit tests for the database and the agent setup logic. The tests are located in the `tests/` directory.
86
-
87
- To run the tests:
88
-
89
- ```bash
90
- # From the project root directory
91
- cd hackathon_organizer
92
- python -m unittest discover tests
93
- ```
94
-
95
- The tests are designed to run without needing an active internet connection or a valid API key. They use mocks to simulate agent behavior and an in-memory database for testing database operations.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hackathon_organizer/hackathon_participants.db DELETED
Binary file (41 kB)
 
hackathon_organizer/requirements.txt DELETED
@@ -1,7 +0,0 @@
1
- tinyagent-py[all]==0.0.12
2
- cloudpickle
3
- modal
4
- jinja2
5
- pyyaml
6
- gradio[mcp]
7
- pandas
 
 
 
 
 
 
 
 
hackathon_participants.db CHANGED
Binary files a/hackathon_participants.db and b/hackathon_participants.db differ
 
main.py DELETED
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Hackathon Team Organizer - Main Entry Point
4
-
5
- This script launches the Hackathon Team Organizer application.
6
- It ensures the working directory is set correctly before running the app.
7
- """
8
- import os
9
- import sys
10
- import subprocess
11
-
12
- def main():
13
- # Get the directory where this script is located
14
- script_dir = os.path.dirname(os.path.abspath(__file__))
15
-
16
- # Change to the script directory to ensure relative paths work
17
- os.chdir(script_dir)
18
-
19
- # Add the current directory to the Python path
20
- sys.path.insert(0, script_dir)
21
-
22
- print("Starting Hackathon Team Organizer...")
23
- print(f"Working directory: {os.getcwd()}")
24
-
25
- # Check for OpenAI API key
26
- if not os.environ.get("OPENAI_API_KEY"):
27
- print("\n⚠️ WARNING: OPENAI_API_KEY environment variable is not set!")
28
- print("The application requires an OpenAI API key to function properly.")
29
- print("Please set it using:")
30
- print(" export OPENAI_API_KEY='your-api-key' (macOS/Linux)")
31
- print(" set OPENAI_API_KEY='your-api-key' (Windows)\n")
32
- print("Running simplified version without AI matching...")
33
- app_script = "hackathon_organizer/app_simple.py"
34
- else:
35
- print("OpenAI API key found. Running full version with AI matching...")
36
- app_script = "hackathon_organizer/app.py"
37
-
38
- # Run the application
39
- try:
40
- print(f"Launching application: {app_script}")
41
- subprocess.run([sys.executable, app_script], check=True)
42
- except KeyboardInterrupt:
43
- print("\nApplication stopped by user.")
44
- except subprocess.CalledProcessError as e:
45
- print(f"\nError running the application: {e}")
46
- sys.exit(1)
47
- except Exception as e:
48
- print(f"\nUnexpected error: {e}")
49
- sys.exit(1)
50
-
51
- if __name__ == "__main__":
52
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hackathon_organizer/matching_agent.py β†’ matching_agent.py RENAMED
File without changes
notebook.ipynb ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": []
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "%load_ext autoreload\n",
17
+ "%autoreload 2\n"
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": 2,
23
+ "metadata": {},
24
+ "outputs": [
25
+ {
26
+ "data": {
27
+ "text/plain": [
28
+ "True"
29
+ ]
30
+ },
31
+ "execution_count": 2,
32
+ "metadata": {},
33
+ "output_type": "execute_result"
34
+ }
35
+ ],
36
+ "source": [
37
+ "import json\n",
38
+ "\n",
39
+ "import os\n",
40
+ "from dotenv import load_dotenv\n",
41
+ "import nest_asyncio\n",
42
+ "nest_asyncio.apply()\n",
43
+ "\n",
44
+ "# Load environment variables from a .env file\n",
45
+ "load_dotenv()"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "code",
50
+ "execution_count": 3,
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "import tinyagent\n",
55
+ "from textwrap import dedent\n",
56
+ "import tinyagent.hooks\n",
57
+ "import tinyagent.hooks.rich_ui_callback\n",
58
+ "import tinyagent.mcp_client\n",
59
+ "import tinyagent.storage.json_file_storage\n",
60
+ "import tinyagent.storage.postgres_storage\n",
61
+ "import tinyagent.storage.sqlite_storage\n",
62
+ "from tinyagent import tool"
63
+ ]
64
+ },
65
+ {
66
+ "cell_type": "code",
67
+ "execution_count": null,
68
+ "metadata": {},
69
+ "outputs": [
70
+ {
71
+ "ename": "SyntaxError",
72
+ "evalue": "invalid syntax (1561902338.py, line 1)",
73
+ "output_type": "error",
74
+ "traceback": [
75
+ " \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mimport .create_matching_agent\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m invalid syntax\n"
76
+ ]
77
+ }
78
+ ],
79
+ "source": [
80
+ "import create_matching_agent"
81
+ ]
82
+ },
83
+ {
84
+ "cell_type": "code",
85
+ "execution_count": null,
86
+ "metadata": {},
87
+ "outputs": [],
88
+ "source": []
89
+ }
90
+ ],
91
+ "metadata": {
92
+ "kernelspec": {
93
+ "display_name": "Python 3.11 (askdev_p311)",
94
+ "language": "python",
95
+ "name": "askdev_p311"
96
+ },
97
+ "language_info": {
98
+ "codemirror_mode": {
99
+ "name": "ipython",
100
+ "version": 3
101
+ },
102
+ "file_extension": ".py",
103
+ "mimetype": "text/x-python",
104
+ "name": "python",
105
+ "nbconvert_exporter": "python",
106
+ "pygments_lexer": "ipython3",
107
+ "version": "3.11.11"
108
+ }
109
+ },
110
+ "nbformat": 4,
111
+ "nbformat_minor": 2
112
+ }
product_manager/organizer.md DELETED
@@ -1,37 +0,0 @@
1
-
2
- #HackBuddyAI
3
-
4
- Gradio APP for Hackathon Organizer
5
-
6
- ## Problem
7
- Many people join hackathons individually, and they need to be matched with other individuals to form a team. If the formed team is not suitable, non-of the team members will benefit from the hackathon, they will churn and not participate in the next hackathon.
8
-
9
- - Help individuals find the right team
10
- - If a selected team is not suitable, or some of participants didn't show up, the organizer could re-run the matching process to find a new team in no time.
11
-
12
- ## Solution
13
- 1. Web application for participants to fill their profile and goals, e_mail field to be used as a unique identifier, link to their linkedin profile, text field (multilined) for their background, skills, why they like to join the hackathon, etc.
14
-
15
- ### 2. On form submission, the data will be stored in a database/ Google Sheet / SQLite
16
-
17
- ### 3. Organizer part:
18
- Organizer could define custom system prompt, to navigate the system for matching the participants, for example group size, why organizer looks to have in each group, etc.
19
-
20
- ### 4. Matching Proccess,
21
- AI Agent, will have access to pandas dataframe, including the participants data and organizer prefrences for matching.
22
- It use its python code execution tool to actually interact with the data, and match the participants based on defined criteria.
23
-
24
- ## End result
25
- List of teams with their members, and a short description why they are a good match. in Markdown format.
26
-
27
-
28
- ## Tech Stack
29
- - Gradio
30
- - Tiny Agent
31
- - Tiny Code Agent
32
- - Pandas
33
-
34
- for developing the agent part we use tiny agent library, you have access to the documentation in the LLM_API_DOC.md file.
35
-
36
- For the frontend we use gradio, gradio has an integration with tiny agent as well.
37
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -3,4 +3,5 @@ cloudpickle
3
  modal
4
  jinja2
5
  pyyaml
6
- gradio[mcp]
 
 
3
  modal
4
  jinja2
5
  pyyaml
6
+ gradio[mcp]
7
+ pandas
{hackathon_organizer/tests β†’ tests}/__init__.py RENAMED
File without changes
tests/test_database.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ import os
3
+ import pandas as pd
4
+ import sys
5
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
+ from database import initialize_database, add_participant, get_participants_dataframe
7
+
8
+ # Use a separate test database
9
+ TEST_DB_PATH = "test_hackathon.db"
10
+
11
+ class TestDatabase(unittest.TestCase):
12
+
13
+ @classmethod
14
+ def setUpClass(cls):
15
+ """Set up a test database before any tests run."""
16
+ cls.db_path = TEST_DB_PATH
17
+ # Point the database module to the test DB
18
+ import database
19
+ database.DB_PATH = cls.db_path
20
+
21
+ def setUp(self):
22
+ """Initialize a clean database before each test."""
23
+ if os.path.exists(self.db_path):
24
+ os.remove(self.db_path)
25
+ initialize_database()
26
+
27
+ def tearDown(self):
28
+ """Remove the database file after each test."""
29
+ if os.path.exists(self.db_path):
30
+ os.remove(self.db_path)
31
+
32
+ def test_01_initialize_database(self):
33
+ """Test that the database and table are created."""
34
+ self.assertTrue(os.path.exists(self.db_path))
35
+ df = get_participants_dataframe()
36
+ self.assertIsInstance(df, pd.DataFrame)
37
+ self.assertEqual(len(df), 0)
38
+
39
+ def test_02_add_and_get_participant(self):
40
+ """Test adding a single participant and retrieving it."""
41
+ participant = {
42
+ "email": "[email protected]",
43
+ "name": "Test User",
44
+ "linkedin_profile": "linkedin.com/in/test",
45
+ "background": "Testing background.",
46
+ "goals": "Testing goals."
47
+ }
48
+ add_participant(participant)
49
+
50
+ df = get_participants_dataframe()
51
+ self.assertEqual(len(df), 1)
52
+
53
+ retrieved = df.iloc[0]
54
+ self.assertEqual(retrieved["email"], participant["email"])
55
+ self.assertEqual(retrieved["name"], participant["name"])
56
+
57
+ def test_03_add_multiple_participants(self):
58
+ """Test adding multiple participants."""
59
+ participants_to_add = [
60
+ {"email": "[email protected]", "name": "User One", "linkedin_profile": "", "background": "", "goals": ""},
61
+ {"email": "[email protected]", "name": "User Two", "linkedin_profile": "", "background": "", "goals": ""},
62
+ ]
63
+ for p in participants_to_add:
64
+ add_participant(p)
65
+
66
+ df = get_participants_dataframe()
67
+ self.assertEqual(len(df), 2)
68
+ self.assertIn("[email protected]", df["email"].values)
69
+ self.assertIn("[email protected]", df["email"].values)
70
+
71
+ def test_04_replace_participant(self):
72
+ """Test that adding a participant with an existing email updates the record."""
73
+ original_participant = {
74
+ "email": "[email protected]",
75
+ "name": "Original Name",
76
+ "linkedin_profile": "original_linkedin",
77
+ "background": "original_background",
78
+ "goals": "original_goals"
79
+ }
80
+ add_participant(original_participant)
81
+
82
+ df_before = get_participants_dataframe()
83
+ self.assertEqual(len(df_before), 1)
84
+ self.assertEqual(df_before.iloc[0]["name"], "Original Name")
85
+
86
+ updated_participant = {
87
+ "email": "[email protected]",
88
+ "name": "Updated Name",
89
+ "linkedin_profile": "updated_linkedin",
90
+ "background": "updated_background",
91
+ "goals": "updated_goals"
92
+ }
93
+ add_participant(updated_participant)
94
+
95
+ df_after = get_participants_dataframe()
96
+ self.assertEqual(len(df_after), 1)
97
+ self.assertEqual(df_after.iloc[0]["name"], "Updated Name")
98
+ self.assertEqual(df_after.iloc[0]["linkedin_profile"], "updated_linkedin")
99
+
100
+ if __name__ == '__main__':
101
+ unittest.main()
tests/test_matching_agent.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock, AsyncMock
3
+ import pandas as pd
4
+ import os
5
+ import sys
6
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
+ from tinyagent import TinyCodeAgent
8
+ from matching_agent import create_matching_agent, run_matching
9
+
10
+ class TestMatchingAgent(unittest.TestCase):
11
+
12
+ @patch.dict(os.environ, {"OPENAI_API_KEY": "test_key"})
13
+ def test_01_create_matching_agent(self):
14
+ """
15
+ Test the creation and configuration of the matching agent.
16
+ """
17
+ agent = create_matching_agent()
18
+
19
+ self.assertIsInstance(agent, TinyCodeAgent)
20
+ self.assertEqual(agent.model, "gpt-4.1-mini")
21
+ self.assertIn("You are a brilliant hackathon team-matching AI.", agent.system_prompt)
22
+ self.assertIn("participants_df", agent.system_prompt)
23
+ self.assertIn("organizer_criteria", agent.system_prompt)
24
+
25
+ # Check code execution environment settings
26
+ self.assertIn("pandas", agent.pip_packages)
27
+ self.assertIn("scikit-learn", agent.pip_packages)
28
+ self.assertIn("pandas", agent.authorized_imports)
29
+
30
+ @patch.dict(os.environ, {"OPENAI_API_KEY": "test_key"})
31
+ def test_02_run_matching(self):
32
+ """
33
+ Test the run_matching function to ensure it configures the agent
34
+ and calls the run method correctly.
35
+ """
36
+ # Create a mock agent to isolate the test from the actual TinyCodeAgent implementation
37
+ mock_agent = MagicMock(spec=TinyCodeAgent)
38
+ mock_agent.run = AsyncMock(return_value="## Mocked Team Report")
39
+
40
+ # Sample data
41
+ participants_df = pd.DataFrame({
42
+ "name": ["Alice", "Bob"],
43
+ "skills": ["Frontend", "Backend"]
44
+ })
45
+ organizer_criteria = "Create teams of 2."
46
+
47
+ # Define an async test function to run the coroutine
48
+ async def do_run_matching():
49
+ result = await run_matching(mock_agent, participants_df, organizer_criteria)
50
+
51
+ # --- Assertions ---
52
+ # 1. Check that user_variables were set correctly on the agent
53
+ self.assertIn("participants_df", mock_agent.user_variables)
54
+ self.assertIn("organizer_criteria", mock_agent.user_variables)
55
+ pd.testing.assert_frame_equal(mock_agent.user_variables["participants_df"], participants_df)
56
+ self.assertEqual(mock_agent.user_variables["organizer_criteria"], organizer_criteria)
57
+
58
+ # 2. Check that agent.run was called correctly
59
+ mock_agent.run.assert_called_once()
60
+ # The first argument to run should be the user prompt
61
+ self.assertEqual(mock_agent.run.call_args[0][0], "Form the teams based on the provided data and criteria.")
62
+
63
+ # 3. Check the result
64
+ self.assertEqual(result, "## Mocked Team Report")
65
+
66
+ # Run the async test function
67
+ import asyncio
68
+ asyncio.run(do_run_matching())
69
+
70
+ if __name__ == '__main__':
71
+ unittest.main()