Spaces:
Sleeping
Sleeping
shyamsridhar123 commited on
Commit Β·
2cb386d
0
Parent(s):
Initial commit: Agentic Skill Builder for MCP Hackathon 2025
Browse files- .env +22 -0
- .gitignore +0 -0
- .gradio/certificate.pem +31 -0
- PRD.md +168 -0
- README.md +300 -0
- README_spaces.md +17 -0
- __pycache__/app.cpython-310.pyc +0 -0
- __pycache__/mcp_server.cpython-310.pyc +0 -0
- __pycache__/space_app.cpython-310.pyc +0 -0
- app.py +647 -0
- config.py +86 -0
- demo_video_script.md +212 -0
- deployment_guide.md +138 -0
- hf-hackathon.code-workspace +8 -0
- mcp_server.py +269 -0
- requirements.txt +9 -0
- run.py +82 -0
- space_app.py +541 -0
- validate_hackathon.py +235 -0
.env
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ο»Ώ# Azure Cosmos DB Configuration
|
| 2 |
+
COSMOS_ENDPOINT=cosmosgremlinai.gremlin.cosmosdb.azure.com
|
| 3 |
+
COSMOS_USERNAME=/dbs/graphdb/colls/ProductsGraph
|
| 4 |
+
COSMOS_PASSWORD=vIoky0D389svaUPQSMqUxf4O3eqQpyOTHWt88izYnVDyLz05q6O7UVEYx7v2pGFBQFGuRWyP6ztlACDbbsMTSw==
|
| 5 |
+
COSMOS_DATABASE=graphdb
|
| 6 |
+
COSMOS_GRAPH=ProductsGraph
|
| 7 |
+
|
| 8 |
+
# Azure OpenAI Configuration
|
| 9 |
+
AZURE_OPENAI_ENDPOINT="https://aihubeastus26267492086.openai.azure.com/"
|
| 10 |
+
AZURE_OPENAI_KEY="2oQRUb4k4Ws3oqaOJyC4ybWCPT3nmTpmIlAXvGzYRvWYFfCDsE8GJQQJ99BEACHYHv6XJ3w3AAAAACOGH87p"
|
| 11 |
+
AZURE_OPENAI_API_VERSION="2024-12-01-preview"
|
| 12 |
+
|
| 13 |
+
# LLM Model Configuration
|
| 14 |
+
AZURE_OPENAI_LLM_DEPLOYMENT="gpt-4.1"
|
| 15 |
+
AZURE_OPENAI_LLM_MODEL="gpt-4.1"
|
| 16 |
+
|
| 17 |
+
# Embeddings Model Configuration
|
| 18 |
+
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT="text-embedding-3-small"
|
| 19 |
+
AZURE_OPENAI_EMBEDDINGS_MODEL="text-embedding-3-small"
|
| 20 |
+
|
| 21 |
+
# Graphiti Configuration
|
| 22 |
+
GRAPHITI_GROUP_NAME="manybirds_graph"
|
.gitignore
ADDED
|
File without changes
|
.gradio/certificate.pem
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN CERTIFICATE-----
|
| 2 |
+
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
| 3 |
+
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
| 4 |
+
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
| 5 |
+
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
| 6 |
+
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
| 7 |
+
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
| 8 |
+
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
| 9 |
+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
| 10 |
+
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
| 11 |
+
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
| 12 |
+
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
| 13 |
+
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
| 14 |
+
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
| 15 |
+
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
| 16 |
+
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
| 17 |
+
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
| 18 |
+
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
| 19 |
+
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
| 20 |
+
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
| 21 |
+
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
| 22 |
+
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
| 23 |
+
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
| 24 |
+
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
| 25 |
+
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
| 26 |
+
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
| 27 |
+
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
| 28 |
+
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
| 29 |
+
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
| 30 |
+
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
| 31 |
+
-----END CERTIFICATE-----
|
PRD.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
# Product Requirements Document (PRD)
|
| 4 |
+
|
| 5 |
+
## Product Name
|
| 6 |
+
|
| 7 |
+
**Agentic Skill Builder**
|
| 8 |
+
|
| 9 |
+
## Purpose
|
| 10 |
+
|
| 11 |
+
Agentic Skill Builder is an AI-powered microlearning platform designed to help users learn new skills through bite-sized lessons and adaptive quizzes. The platform leverages Azure OpenAI for content generation, Gradio for user interaction, and Model Context Protocol (MCP) for agent interoperability.
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## 1. Objectives
|
| 16 |
+
|
| 17 |
+
- **Deliver Personalized Microlearning:** Provide users with concise, high-quality lessons and adaptive quizzes tailored to their chosen skill.
|
| 18 |
+
- **Showcase Agentic Workflows:** Demonstrate how multiple AI agents (lesson generator, quiz generator, progress tracker) can collaborate to enhance learning.
|
| 19 |
+
- **Enable Interoperability via MCP:** Allow external agents and applications to interact with the learning modules and user progress through MCP endpoints.
|
| 20 |
+
- **Offer a Polished, User-Friendly Interface:** Use Gradio to deliver an intuitive, engaging, and accessible experience.
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## 2. Target Users
|
| 25 |
+
|
| 26 |
+
- **Lifelong Learners:** Individuals seeking to acquire or reinforce skills in short, focused sessions.
|
| 27 |
+
- **Hackathon Participants:** Developers and researchers interested in agentic workflows and MCP integration.
|
| 28 |
+
- **Educational Institutions:** Teachers and trainers looking for AI-driven microlearning tools.
|
| 29 |
+
- **Integration Developers:** Teams building apps that could benefit from plug-and-play learning modules.
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## 3. Features \& Requirements
|
| 34 |
+
|
| 35 |
+
### 3.1 Core Features
|
| 36 |
+
|
| 37 |
+
#### 3.1.1 Skill Selection
|
| 38 |
+
|
| 39 |
+
- Users can select from a list of predefined skills (e.g., Python, Spanish, Public Speaking) or enter a custom skill/topic.
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
#### 3.1.2 Micro-Lesson Delivery
|
| 43 |
+
|
| 44 |
+
- For the chosen skill, the system generates and presents a concise, focused lesson (text, optionally with links to videos or code snippets).
|
| 45 |
+
- Lessons are generated dynamically using Azure OpenAI.
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
#### 3.1.3 Adaptive Quiz
|
| 49 |
+
|
| 50 |
+
- After each lesson, users receive a short quiz (e.g., multiple choice, fill-in-the-blank) tailored to the lesson content.
|
| 51 |
+
- The quiz adapts in difficulty based on user performance over time.
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
#### 3.1.4 Progress Tracking
|
| 55 |
+
|
| 56 |
+
- The system tracks user progress (e.g., lessons completed, quiz accuracy, streaks).
|
| 57 |
+
- Progress is displayed visually (e.g., progress bars, charts).
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
#### 3.1.5 Recommendations
|
| 61 |
+
|
| 62 |
+
- Based on performance, the system recommends the next lesson, a review session, or an increased difficulty level.
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
### 3.2 Agentic Architecture
|
| 66 |
+
|
| 67 |
+
- **Lesson Agent:** Generates concise lessons for the selected skill.
|
| 68 |
+
- **Quiz Agent:** Creates contextually relevant quizzes based on the lesson.
|
| 69 |
+
- **Progress Agent:** Monitors and updates user progress, provides feedback, and recommends next steps.
|
| 70 |
+
- **Orchestrator:** Coordinates the flow between agents and the user interface.
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
### 3.3 MCP Integration
|
| 74 |
+
|
| 75 |
+
- Expose endpoints for:
|
| 76 |
+
- Fetching the next lesson for a user/skill.
|
| 77 |
+
- Retrieving user progress data.
|
| 78 |
+
- Submitting quiz results.
|
| 79 |
+
- Ensure endpoints are documented and compatible with the Model Context Protocol.
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
### 3.4 User Interface
|
| 83 |
+
|
| 84 |
+
- **Built with Gradio:**
|
| 85 |
+
- Step-by-step workflow: Skill selection β Lesson β Quiz β Feedback/Progress.
|
| 86 |
+
- Clean, accessible design with clear navigation.
|
| 87 |
+
- Responsive for desktop and mobile.
|
| 88 |
+
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## 4. Non-Functional Requirements
|
| 92 |
+
|
| 93 |
+
- **Performance:** Lessons and quizzes should be generated in under 5 seconds.
|
| 94 |
+
- **Scalability:** Support at least 100 concurrent users for demo purposes.
|
| 95 |
+
- **Security:** User data (progress, answers) is stored securely and not shared without consent.
|
| 96 |
+
- **Accessibility:** UI should be usable with screen readers and keyboard navigation.
|
| 97 |
+
- **Reliability:** System should handle API failures gracefully and provide user-friendly error messages.
|
| 98 |
+
|
| 99 |
+
---
|
| 100 |
+
|
| 101 |
+
## 5. Optional \& Stretch Features
|
| 102 |
+
|
| 103 |
+
- **Speech-to-Text Input:** For language practice, allow users to answer quizzes verbally.
|
| 104 |
+
- **Leaderboard:** Display top learners (opt-in).
|
| 105 |
+
- **Daily Reminders:** Send notifications or emails to encourage regular learning.
|
| 106 |
+
- **Custom Content Upload:** Allow educators to add their own lesson modules.
|
| 107 |
+
- **Multi-modal Lessons:** Incorporate images, audio, or video if supported by Azure OpenAI.
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## 6. Technical Stack
|
| 112 |
+
|
| 113 |
+
- **Backend:** Azure OpenAI (GPT-3.5, GPT-4, or GPT-4o)
|
| 114 |
+
- **Frontend:** Gradio (Python)
|
| 115 |
+
- **MCP Integration:** Gradio MCP server functionality
|
| 116 |
+
- **Data Storage:** In-memory or lightweight database (for hackathon demo)
|
| 117 |
+
- **Deployment:** Hugging Face Spaces or Azure App Service
|
| 118 |
+
|
| 119 |
+
---
|
| 120 |
+
|
| 121 |
+
## 7. Success Metrics
|
| 122 |
+
|
| 123 |
+
- **User Engagement:** Number of lessons/quizzes completed per user.
|
| 124 |
+
- **Learning Outcomes:** Improvement in quiz scores over sessions.
|
| 125 |
+
- **MCP Usage:** Number of successful external calls to MCP endpoints.
|
| 126 |
+
- **User Satisfaction:** Positive feedback from hackathon judges and users.
|
| 127 |
+
|
| 128 |
+
---
|
| 129 |
+
|
| 130 |
+
## 8. Risks \& Mitigations
|
| 131 |
+
|
| 132 |
+
| Risk | Mitigation |
|
| 133 |
+
| :-- | :-- |
|
| 134 |
+
| Slow response from Azure OpenAI | Cache common lessons/quizzes, optimize prompts |
|
| 135 |
+
| User data loss (demo) | Regular backups, clear communication |
|
| 136 |
+
| MCP integration complexity | Use official Gradio MCP templates and docs |
|
| 137 |
+
| Overly generic lessons/quizzes | Refine prompts, add manual review if possible |
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
---
|
| 141 |
+
|
| 142 |
+
## 9. Milestones \& Timeline
|
| 143 |
+
|
| 144 |
+
| Milestone | Target Date |
|
| 145 |
+
| :-- | :-- |
|
| 146 |
+
| Project setup \& Azure OpenAI config | Day 1 |
|
| 147 |
+
| Core agent logic implemented | Day 2 |
|
| 148 |
+
| Gradio UI complete | Day 3 |
|
| 149 |
+
| MCP endpoints exposed \& tested | Day 4 |
|
| 150 |
+
| Polish, optional features, testing | Day 5 |
|
| 151 |
+
| Submission \& documentation | Day 6 |
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
---
|
| 155 |
+
|
| 156 |
+
## 10. Appendix
|
| 157 |
+
|
| 158 |
+
- **References:**
|
| 159 |
+
- [Gradio Documentation](https://www.gradio.app/)
|
| 160 |
+
- [Azure OpenAI Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/)
|
| 161 |
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
| 162 |
+
- **Contact:**
|
| 163 |
+
- Hackathon team email/slack/discord
|
| 164 |
+
|
| 165 |
+
---
|
| 166 |
+
|
| 167 |
+
**This PRD is designed for clarity, feasibility, and alignment with hackathon goals. Let me know if you need a version tailored for a specific audience (e.g., business, technical, or educational) or want to add/remove features!**
|
| 168 |
+
|
README.md
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Agentic Skill Builder - MCP Hackathon 2025
|
| 3 |
+
emoji: π
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 4.44.0
|
| 8 |
+
app_file: space_app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
tags:
|
| 12 |
+
- mcp-server-track
|
| 13 |
+
- agents
|
| 14 |
+
- education
|
| 15 |
+
- microlearning
|
| 16 |
+
- azure-openai
|
| 17 |
+
- model-context-protocol
|
| 18 |
+
short_description: AI-powered microlearning platform with MCP integration for the Gradio Agents & MCP Hackathon 2025
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
# π Agentic Skill Builder
|
| 22 |
+
|
| 23 |
+
**Track:** mcp-server-track
|
| 24 |
+
|
| 25 |
+
An AI-powered microlearning platform that leverages Azure OpenAI, Gradio, and Model Context Protocol (MCP) to deliver personalized bite-sized lessons and adaptive quizzes.
|
| 26 |
+
|
| 27 |
+
π **Submitted for the Gradio Agents & MCP Hackathon 2025** π
|
| 28 |
+
|
| 29 |
+
## π¬ Demo Video
|
| 30 |
+
|
| 31 |
+
**MCP Server in Action:** [Demo Video Link](https://your-demo-video-link.com)
|
| 32 |
+
|
| 33 |
+
*Note: The video demonstrates the MCP server endpoints being used by various MCP clients, showcasing the seamless integration between the Gradio interface and Model Context Protocol functionality.*
|
| 34 |
+
|
| 35 |
+
## π Hackathon Highlights
|
| 36 |
+
|
| 37 |
+
This submission demonstrates several key innovations for the **Gradio Agents & MCP Hackathon 2025**:
|
| 38 |
+
|
| 39 |
+
### π€ **Track 1: MCP Server/Tool**
|
| 40 |
+
- β
**Dual-Purpose Application**: Single app serving both Gradio interface AND MCP server
|
| 41 |
+
- β
**Full MCP Protocol Implementation**: Complete endpoints for lesson generation, progress tracking, and quiz submission
|
| 42 |
+
- β
**External Agent Integration**: Ready for use by Claude Desktop, Cursor, or any MCP client
|
| 43 |
+
|
| 44 |
+
### π§ **Agentic Architecture Innovation**
|
| 45 |
+
- **π Lesson Agent**: AI-powered content generation with Azure OpenAI
|
| 46 |
+
- **π§ͺ Quiz Agent**: Adaptive quiz creation based on lesson content and user performance
|
| 47 |
+
- **π Progress Agent**: Smart difficulty adjustment and learning recommendations
|
| 48 |
+
- **π― Orchestrator**: Seamless coordination between all agents and user interactions
|
| 49 |
+
|
| 50 |
+
### π **MCP Endpoints Showcase**
|
| 51 |
+
- `GET /mcp/skills` - Discover available learning skills
|
| 52 |
+
- `POST /mcp/lesson/generate` - Generate personalized micro-lessons
|
| 53 |
+
- `GET /mcp/progress/{user_id}` - Access detailed learning analytics
|
| 54 |
+
- `POST /mcp/quiz/submit` - Submit and score quiz attempts
|
| 55 |
+
|
| 56 |
+
### π‘ **Unique Features**
|
| 57 |
+
- **Microlearning Focus**: 3-5 minute bite-sized lessons perfect for busy learners
|
| 58 |
+
- **Adaptive Difficulty**: AI automatically adjusts based on quiz performance
|
| 59 |
+
- **Any Skill Learning**: Works for both predefined and custom skills
|
| 60 |
+
- **Real-time Analytics**: Live progress tracking and personalized recommendations
|
| 61 |
+
|
| 62 |
+
## β¨ Features
|
| 63 |
+
|
| 64 |
+
- π― **Skill Selection**: Choose from predefined skills or enter custom topics
|
| 65 |
+
- π **AI-Generated Micro-Lessons**: Concise, focused lessons (3-5 minutes)
|
| 66 |
+
- π§ **Adaptive Quizzes**: Smart quizzes that adjust difficulty based on performance
|
| 67 |
+
- π **Progress Tracking**: Visual progress monitoring and analytics
|
| 68 |
+
- π€ **Agentic Architecture**: Multiple specialized AI agents working together
|
| 69 |
+
- π **MCP Integration**: Model Context Protocol endpoints for external integration
|
| 70 |
+
- π¨ **Modern UI**: Clean, responsive Gradio interface
|
| 71 |
+
|
| 72 |
+
## ποΈ Architecture
|
| 73 |
+
|
| 74 |
+
The application uses an agentic architecture with specialized AI agents:
|
| 75 |
+
|
| 76 |
+
- **Lesson Agent**: Generates personalized micro-lessons
|
| 77 |
+
- **Quiz Agent**: Creates adaptive quizzes based on lesson content
|
| 78 |
+
- **Progress Agent**: Tracks learning progress and provides recommendations
|
| 79 |
+
- **Orchestrator**: Coordinates agent interactions and user flow
|
| 80 |
+
|
| 81 |
+
## π Quick Start
|
| 82 |
+
|
| 83 |
+
### Prerequisites
|
| 84 |
+
|
| 85 |
+
- **Python 3.10.16** installed
|
| 86 |
+
- Azure OpenAI subscription with API key
|
| 87 |
+
- Access to GPT-4 model deployment
|
| 88 |
+
|
| 89 |
+
### Installation
|
| 90 |
+
|
| 91 |
+
1. **Clone and navigate to the project:**
|
| 92 |
+
```powershell
|
| 93 |
+
cd c:\Users\shyamsridhar\code\hf-hackathon
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
2. **Create and activate a virtual environment:**
|
| 97 |
+
```powershell
|
| 98 |
+
python -m venv .venv
|
| 99 |
+
.venv\Scripts\Activate
|
| 100 |
+
```
|
| 101 |
+
*(On macOS/Linux, use `source .venv/bin/activate`)*
|
| 102 |
+
|
| 103 |
+
3. **Install dependencies:**
|
| 104 |
+
```powershell
|
| 105 |
+
pip install -r requirements.txt
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
4. **Configure environment variables:**
|
| 109 |
+
- Your `.env` file is already configured with Azure OpenAI credentials
|
| 110 |
+
- Verify the credentials are correct and models are deployed
|
| 111 |
+
|
| 112 |
+
5. **Run the application:**
|
| 113 |
+
```powershell
|
| 114 |
+
python run.py
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
Choose from three options:
|
| 118 |
+
- **Option 1**: Gradio App only (recommended for demo)
|
| 119 |
+
- **Option 2**: MCP Server only
|
| 120 |
+
- **Option 3**: Both services
|
| 121 |
+
|
| 122 |
+
## π― Usage
|
| 123 |
+
|
| 124 |
+
### Learning Flow
|
| 125 |
+
|
| 126 |
+
1. **Select a Skill**: Choose from predefined skills or enter a custom topic
|
| 127 |
+
2. **Read the Lesson**: Engage with AI-generated micro-content
|
| 128 |
+
3. **Take the Quiz**: Test your understanding with adaptive questions
|
| 129 |
+
4. **View Results**: Get detailed feedback and progress updates
|
| 130 |
+
5. **Continue Learning**: Follow AI recommendations for next steps
|
| 131 |
+
|
| 132 |
+
### Available Skills
|
| 133 |
+
|
| 134 |
+
- Python Programming
|
| 135 |
+
- Spanish Language
|
| 136 |
+
- Public Speaking
|
| 137 |
+
- Data Science
|
| 138 |
+
- Machine Learning
|
| 139 |
+
- JavaScript
|
| 140 |
+
- Project Management
|
| 141 |
+
- Digital Marketing
|
| 142 |
+
- Creative Writing
|
| 143 |
+
- Photography
|
| 144 |
+
|
| 145 |
+
*Plus any custom skill you can imagine!*
|
| 146 |
+
|
| 147 |
+
## π MCP Endpoints
|
| 148 |
+
|
| 149 |
+
The application exposes Model Context Protocol endpoints at `http://localhost:8000`:
|
| 150 |
+
|
| 151 |
+
- `GET /skills` - List available skills
|
| 152 |
+
- `POST /lesson/generate` - Generate lesson for a skill
|
| 153 |
+
- `GET /progress/{user_id}` - Get user progress data
|
| 154 |
+
- `POST /quiz/submit` - Submit quiz results
|
| 155 |
+
- `POST /quiz/generate` - Generate quiz for a lesson
|
| 156 |
+
|
| 157 |
+
### API Documentation
|
| 158 |
+
|
| 159 |
+
Visit `http://localhost:8000/docs` for interactive API documentation.
|
| 160 |
+
|
| 161 |
+
## π Progress Dashboard
|
| 162 |
+
|
| 163 |
+
Track your learning journey with:
|
| 164 |
+
|
| 165 |
+
- **Lessons Completed**: Number of lessons finished per skill
|
| 166 |
+
- **Quiz Performance**: Average scores and improvement trends
|
| 167 |
+
- **Difficulty Progression**: Automatic difficulty adjustment
|
| 168 |
+
- **Learning Streaks**: Consistent learning tracking
|
| 169 |
+
- **AI Recommendations**: Personalized next steps
|
| 170 |
+
|
| 171 |
+
## π§ Configuration
|
| 172 |
+
|
| 173 |
+
### Environment Variables
|
| 174 |
+
|
| 175 |
+
The `.env` file contains your Azure OpenAI configuration:
|
| 176 |
+
|
| 177 |
+
```properties
|
| 178 |
+
# Azure OpenAI Configuration
|
| 179 |
+
AZURE_OPENAI_ENDPOINT="your-endpoint"
|
| 180 |
+
AZURE_OPENAI_KEY="your-api-key"
|
| 181 |
+
AZURE_OPENAI_API_VERSION="2024-12-01-preview"
|
| 182 |
+
AZURE_OPENAI_LLM_DEPLOYMENT="gpt-4.1"
|
| 183 |
+
AZURE_OPENAI_LLM_MODEL="gpt-4.1"
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
### Optional Settings
|
| 187 |
+
|
| 188 |
+
You can add these optional environment variables:
|
| 189 |
+
|
| 190 |
+
```properties
|
| 191 |
+
DEBUG=false
|
| 192 |
+
LOG_LEVEL=INFO
|
| 193 |
+
GRADIO_PORT=7860
|
| 194 |
+
MCP_PORT=8000
|
| 195 |
+
MAX_QUIZ_QUESTIONS=5
|
| 196 |
+
DEFAULT_LESSON_DURATION=5
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
## π§ͺ Development
|
| 200 |
+
|
| 201 |
+
### Running in Development Mode
|
| 202 |
+
|
| 203 |
+
```powershell
|
| 204 |
+
# Install development dependencies
|
| 205 |
+
pip install -r requirements.txt
|
| 206 |
+
|
| 207 |
+
# Run with auto-reload
|
| 208 |
+
python app.py
|
| 209 |
+
```
|
| 210 |
+
|
| 211 |
+
### Testing MCP Endpoints
|
| 212 |
+
|
| 213 |
+
```bash
|
| 214 |
+
# Test lesson generation
|
| 215 |
+
curl -X POST "http://localhost:8000/lesson/generate" \
|
| 216 |
+
-H "Content-Type: application/json" \
|
| 217 |
+
-d '{"skill": "Python Programming", "user_id": "test_user"}'
|
| 218 |
+
|
| 219 |
+
# Get user progress
|
| 220 |
+
curl "http://localhost:8000/progress/test_user?skill=Python%20Programming"
|
| 221 |
+
```
|
| 222 |
+
|
| 223 |
+
## π± Deployment
|
| 224 |
+
|
| 225 |
+
### Local Deployment
|
| 226 |
+
|
| 227 |
+
```powershell
|
| 228 |
+
python run.py
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
### Hugging Face Spaces
|
| 232 |
+
|
| 233 |
+
1. Create a new Space on Hugging Face
|
| 234 |
+
2. Upload your code
|
| 235 |
+
3. Set environment variables in Space settings
|
| 236 |
+
4. The app will auto-deploy
|
| 237 |
+
|
| 238 |
+
### Azure App Service
|
| 239 |
+
|
| 240 |
+
1. Create an Azure App Service
|
| 241 |
+
2. Deploy using Git or ZIP
|
| 242 |
+
3. Configure environment variables
|
| 243 |
+
4. Set startup command: `python app.py`
|
| 244 |
+
|
| 245 |
+
## π Troubleshooting
|
| 246 |
+
|
| 247 |
+
### Common Issues
|
| 248 |
+
|
| 249 |
+
1. **Azure OpenAI Connection Error**
|
| 250 |
+
- Verify your endpoint URL and API key
|
| 251 |
+
- Check if your model deployment is active
|
| 252 |
+
- Ensure you have sufficient quota
|
| 253 |
+
|
| 254 |
+
2. **Module Import Errors**
|
| 255 |
+
- Activate your virtual environment
|
| 256 |
+
- Install requirements: `pip install -r requirements.txt`
|
| 257 |
+
|
| 258 |
+
3. **Port Already in Use**
|
| 259 |
+
- Change ports in environment variables
|
| 260 |
+
- Kill existing processes: `netstat -ano | findstr :7860`
|
| 261 |
+
|
| 262 |
+
### Logs
|
| 263 |
+
|
| 264 |
+
Check application logs in `agentic_skill_builder.log` for detailed error information.
|
| 265 |
+
|
| 266 |
+
## π€ Contributing
|
| 267 |
+
|
| 268 |
+
1. Fork the repository
|
| 269 |
+
2. Create a feature branch
|
| 270 |
+
3. Make your changes
|
| 271 |
+
4. Test thoroughly
|
| 272 |
+
5. Submit a pull request
|
| 273 |
+
|
| 274 |
+
## π License
|
| 275 |
+
|
| 276 |
+
This project is part of a hackathon submission. See the full requirements in `i dont want the code but a detailed prd document.md`.
|
| 277 |
+
|
| 278 |
+
## π Hackathon Features
|
| 279 |
+
|
| 280 |
+
This implementation demonstrates:
|
| 281 |
+
|
| 282 |
+
- β
**Agentic Workflows**: Multiple AI agents collaborating
|
| 283 |
+
- β
**Azure OpenAI Integration**: Modern SDK usage with best practices
|
| 284 |
+
- β
**Adaptive Learning**: Smart difficulty adjustment
|
| 285 |
+
- β
**Modern UI**: Gradio-based responsive interface
|
| 286 |
+
- β
**MCP Protocol**: External agent integration capability
|
| 287 |
+
- β
**Progress Analytics**: Comprehensive learning tracking
|
| 288 |
+
- β
**Error Handling**: Robust error management and fallbacks
|
| 289 |
+
|
| 290 |
+
## π Support
|
| 291 |
+
|
| 292 |
+
For issues or questions:
|
| 293 |
+
1. Check the troubleshooting section
|
| 294 |
+
2. Review the logs in `agentic_skill_builder.log`
|
| 295 |
+
3. Verify your Azure OpenAI configuration
|
| 296 |
+
4. Ensure all dependencies are installed correctly
|
| 297 |
+
|
| 298 |
+
---
|
| 299 |
+
|
| 300 |
+
**Happy Learning! π**
|
README_spaces.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
title: Agentic Skill Builder - MCP Hackathon 2025
|
| 2 |
+
emoji: π
|
| 3 |
+
colorFrom: blue
|
| 4 |
+
colorTo: purple
|
| 5 |
+
sdk: gradio
|
| 6 |
+
sdk_version: 4.44.0
|
| 7 |
+
app_file: space_app.py
|
| 8 |
+
pinned: false
|
| 9 |
+
license: mit
|
| 10 |
+
tags:
|
| 11 |
+
- mcp-server-track
|
| 12 |
+
- agents
|
| 13 |
+
- education
|
| 14 |
+
- microlearning
|
| 15 |
+
- azure-openai
|
| 16 |
+
- model-context-protocol
|
| 17 |
+
short_description: AI-powered microlearning platform with MCP integration for the Gradio Agents & MCP Hackathon 2025
|
__pycache__/app.cpython-310.pyc
ADDED
|
Binary file (20.7 kB). View file
|
|
|
__pycache__/mcp_server.cpython-310.pyc
ADDED
|
Binary file (6.7 kB). View file
|
|
|
__pycache__/space_app.cpython-310.pyc
ADDED
|
Binary file (15.8 kB). View file
|
|
|
app.py
ADDED
|
@@ -0,0 +1,647 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import asyncio
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from typing import Dict, List, Optional, Tuple
|
| 6 |
+
from dataclasses import dataclass, asdict
|
| 7 |
+
import logging
|
| 8 |
+
|
| 9 |
+
from dotenv import load_dotenv
|
| 10 |
+
import gradio as gr
|
| 11 |
+
from openai import AzureOpenAI
|
| 12 |
+
import pandas as pd
|
| 13 |
+
|
| 14 |
+
# Configure logging
|
| 15 |
+
logging.basicConfig(level=logging.INFO)
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
# Load environment variables
|
| 19 |
+
load_dotenv()
|
| 20 |
+
|
| 21 |
+
# Azure OpenAI client configuration
|
| 22 |
+
client = AzureOpenAI(
|
| 23 |
+
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT", "").replace('"', ''),
|
| 24 |
+
api_key=os.getenv("AZURE_OPENAI_KEY", "").replace('"', ''),
|
| 25 |
+
api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-12-01-preview").replace('"', ''),
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# Model configurations
|
| 29 |
+
LLM_DEPLOYMENT = os.getenv("AZURE_OPENAI_LLM_DEPLOYMENT", "gpt-4.1").replace('"', '')
|
| 30 |
+
LLM_MODEL = os.getenv("AZURE_OPENAI_LLM_MODEL", "gpt-4.1").replace('"', '')
|
| 31 |
+
|
| 32 |
+
@dataclass
|
| 33 |
+
class UserProgress:
|
| 34 |
+
"""Data class to track user learning progress"""
|
| 35 |
+
user_id: str
|
| 36 |
+
skill: str
|
| 37 |
+
lessons_completed: int = 0
|
| 38 |
+
quiz_scores: List[float] = None
|
| 39 |
+
current_difficulty: str = "beginner"
|
| 40 |
+
streak_days: int = 0
|
| 41 |
+
total_time_spent: int = 0 # minutes
|
| 42 |
+
last_activity: str = ""
|
| 43 |
+
|
| 44 |
+
def __post_init__(self):
|
| 45 |
+
if self.quiz_scores is None:
|
| 46 |
+
self.quiz_scores = []
|
| 47 |
+
|
| 48 |
+
def get_average_score(self) -> float:
|
| 49 |
+
"""Calculate average quiz score"""
|
| 50 |
+
return sum(self.quiz_scores) / len(self.quiz_scores) if self.quiz_scores else 0.0
|
| 51 |
+
|
| 52 |
+
def add_quiz_score(self, score: float):
|
| 53 |
+
"""Add a new quiz score and update difficulty if needed"""
|
| 54 |
+
self.quiz_scores.append(score)
|
| 55 |
+
# Adaptive difficulty adjustment
|
| 56 |
+
avg_score = self.get_average_score()
|
| 57 |
+
if avg_score >= 0.8 and len(self.quiz_scores) >= 3:
|
| 58 |
+
if self.current_difficulty == "beginner":
|
| 59 |
+
self.current_difficulty = "intermediate"
|
| 60 |
+
elif self.current_difficulty == "intermediate":
|
| 61 |
+
self.current_difficulty = "advanced"
|
| 62 |
+
elif avg_score < 0.6 and len(self.quiz_scores) >= 3:
|
| 63 |
+
if self.current_difficulty == "advanced":
|
| 64 |
+
self.current_difficulty = "intermediate"
|
| 65 |
+
elif self.current_difficulty == "intermediate":
|
| 66 |
+
self.current_difficulty = "beginner"
|
| 67 |
+
|
| 68 |
+
@dataclass
|
| 69 |
+
class Lesson:
|
| 70 |
+
"""Data class for lesson content"""
|
| 71 |
+
title: str
|
| 72 |
+
content: str
|
| 73 |
+
skill: str
|
| 74 |
+
difficulty: str
|
| 75 |
+
duration_minutes: int
|
| 76 |
+
key_concepts: List[str]
|
| 77 |
+
|
| 78 |
+
@dataclass
|
| 79 |
+
class Quiz:
|
| 80 |
+
"""Data class for quiz content"""
|
| 81 |
+
questions: List[Dict]
|
| 82 |
+
skill: str
|
| 83 |
+
difficulty: str
|
| 84 |
+
lesson_title: str
|
| 85 |
+
|
| 86 |
+
class LessonAgent:
|
| 87 |
+
"""Agent responsible for generating personalized micro-lessons"""
|
| 88 |
+
|
| 89 |
+
def __init__(self, client: AzureOpenAI):
|
| 90 |
+
self.client = client
|
| 91 |
+
self.model = LLM_DEPLOYMENT
|
| 92 |
+
|
| 93 |
+
async def generate_lesson(self, skill: str, difficulty: str = "beginner",
|
| 94 |
+
previous_lessons: List[str] = None) -> Lesson:
|
| 95 |
+
"""Generate a personalized micro-lesson"""
|
| 96 |
+
try:
|
| 97 |
+
previous_context = ""
|
| 98 |
+
if previous_lessons:
|
| 99 |
+
previous_context = f"\nPrevious lessons covered: {', '.join(previous_lessons[-3:])}"
|
| 100 |
+
|
| 101 |
+
prompt = f"""
|
| 102 |
+
Create a concise, engaging micro-lesson for the skill: {skill}
|
| 103 |
+
Difficulty level: {difficulty}
|
| 104 |
+
{previous_context}
|
| 105 |
+
|
| 106 |
+
Requirements:
|
| 107 |
+
- Lesson should be 3-5 minutes to read
|
| 108 |
+
- Include practical examples
|
| 109 |
+
- Focus on one key concept
|
| 110 |
+
- Make it actionable
|
| 111 |
+
- Include 3-5 key takeaways
|
| 112 |
+
|
| 113 |
+
Format your response as JSON with these fields:
|
| 114 |
+
{{
|
| 115 |
+
"title": "Lesson title",
|
| 116 |
+
"content": "Main lesson content (200-400 words)",
|
| 117 |
+
"duration_minutes": 4,
|
| 118 |
+
"key_concepts": ["concept1", "concept2", "concept3"]
|
| 119 |
+
}}
|
| 120 |
+
"""
|
| 121 |
+
|
| 122 |
+
response = self.client.chat.completions.create(
|
| 123 |
+
model=self.model,
|
| 124 |
+
messages=[
|
| 125 |
+
{"role": "system", "content": "You are an expert educator creating micro-lessons. Always respond with valid JSON."},
|
| 126 |
+
{"role": "user", "content": prompt}
|
| 127 |
+
],
|
| 128 |
+
temperature=0.7,
|
| 129 |
+
max_tokens=1000
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
lesson_data = json.loads(response.choices[0].message.content)
|
| 133 |
+
|
| 134 |
+
return Lesson(
|
| 135 |
+
title=lesson_data["title"],
|
| 136 |
+
content=lesson_data["content"],
|
| 137 |
+
skill=skill,
|
| 138 |
+
difficulty=difficulty,
|
| 139 |
+
duration_minutes=lesson_data["duration_minutes"],
|
| 140 |
+
key_concepts=lesson_data["key_concepts"]
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
except Exception as e:
|
| 144 |
+
logger.error(f"Error generating lesson: {e}")
|
| 145 |
+
# Fallback lesson
|
| 146 |
+
return Lesson(
|
| 147 |
+
title=f"Introduction to {skill}",
|
| 148 |
+
content=f"Let's start learning about {skill}. This is a fundamental skill that can help you grow professionally and personally.",
|
| 149 |
+
skill=skill,
|
| 150 |
+
difficulty=difficulty,
|
| 151 |
+
duration_minutes=3,
|
| 152 |
+
key_concepts=["basics", "fundamentals", "getting started"]
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
class QuizAgent:
|
| 156 |
+
"""Agent responsible for generating adaptive quizzes"""
|
| 157 |
+
|
| 158 |
+
def __init__(self, client: AzureOpenAI):
|
| 159 |
+
self.client = client
|
| 160 |
+
self.model = LLM_DEPLOYMENT
|
| 161 |
+
|
| 162 |
+
async def generate_quiz(self, lesson: Lesson, user_progress: UserProgress) -> Quiz:
|
| 163 |
+
"""Generate an adaptive quiz based on the lesson content"""
|
| 164 |
+
try:
|
| 165 |
+
avg_score = user_progress.get_average_score()
|
| 166 |
+
|
| 167 |
+
prompt = f"""
|
| 168 |
+
Create a quiz for this lesson:
|
| 169 |
+
Title: {lesson.title}
|
| 170 |
+
Content: {lesson.content}
|
| 171 |
+
Key concepts: {', '.join(lesson.key_concepts)}
|
| 172 |
+
|
| 173 |
+
User's average score: {avg_score:.1f}
|
| 174 |
+
Current difficulty: {lesson.difficulty}
|
| 175 |
+
|
| 176 |
+
Create 3-5 questions that test understanding of the lesson.
|
| 177 |
+
Mix question types: multiple choice, true/false, and short answer.
|
| 178 |
+
|
| 179 |
+
Format as JSON:
|
| 180 |
+
{{
|
| 181 |
+
"questions": [
|
| 182 |
+
{{
|
| 183 |
+
"type": "multiple_choice",
|
| 184 |
+
"question": "Question text?",
|
| 185 |
+
"options": ["A", "B", "C", "D"],
|
| 186 |
+
"correct_answer": "A",
|
| 187 |
+
"explanation": "Why this is correct"
|
| 188 |
+
}},
|
| 189 |
+
{{
|
| 190 |
+
"type": "true_false",
|
| 191 |
+
"question": "Statement to evaluate",
|
| 192 |
+
"correct_answer": true,
|
| 193 |
+
"explanation": "Explanation"
|
| 194 |
+
}}
|
| 195 |
+
]
|
| 196 |
+
}}
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
response = self.client.chat.completions.create(
|
| 200 |
+
model=self.model,
|
| 201 |
+
messages=[
|
| 202 |
+
{"role": "system", "content": "You are a quiz expert. Always respond with valid JSON."},
|
| 203 |
+
{"role": "user", "content": prompt}
|
| 204 |
+
],
|
| 205 |
+
temperature=0.7,
|
| 206 |
+
max_tokens=1200
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
quiz_data = json.loads(response.choices[0].message.content)
|
| 210 |
+
|
| 211 |
+
return Quiz(
|
| 212 |
+
questions=quiz_data["questions"],
|
| 213 |
+
skill=lesson.skill,
|
| 214 |
+
difficulty=lesson.difficulty,
|
| 215 |
+
lesson_title=lesson.title
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
except Exception as e:
|
| 219 |
+
logger.error(f"Error generating quiz: {e}")
|
| 220 |
+
# Fallback quiz
|
| 221 |
+
return Quiz(
|
| 222 |
+
questions=[{
|
| 223 |
+
"type": "multiple_choice",
|
| 224 |
+
"question": f"What is the main topic of this lesson about {lesson.skill}?",
|
| 225 |
+
"options": [lesson.skill, "Something else", "Not sure", "All of the above"],
|
| 226 |
+
"correct_answer": lesson.skill,
|
| 227 |
+
"explanation": f"This lesson focuses on {lesson.skill}"
|
| 228 |
+
}],
|
| 229 |
+
skill=lesson.skill,
|
| 230 |
+
difficulty=lesson.difficulty,
|
| 231 |
+
lesson_title=lesson.title
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
class ProgressAgent:
|
| 235 |
+
"""Agent responsible for tracking progress and making recommendations"""
|
| 236 |
+
|
| 237 |
+
def __init__(self):
|
| 238 |
+
self.user_data: Dict[str, UserProgress] = {}
|
| 239 |
+
|
| 240 |
+
def get_user_progress(self, user_id: str, skill: str) -> UserProgress:
|
| 241 |
+
"""Get or create user progress tracking"""
|
| 242 |
+
key = f"{user_id}_{skill}"
|
| 243 |
+
if key not in self.user_data:
|
| 244 |
+
self.user_data[key] = UserProgress(user_id=user_id, skill=skill)
|
| 245 |
+
return self.user_data[key]
|
| 246 |
+
|
| 247 |
+
def update_progress(self, user_id: str, skill: str, lesson_completed: bool = False,
|
| 248 |
+
quiz_score: float = None) -> UserProgress:
|
| 249 |
+
"""Update user progress after lesson/quiz completion"""
|
| 250 |
+
progress = self.get_user_progress(user_id, skill)
|
| 251 |
+
|
| 252 |
+
if lesson_completed:
|
| 253 |
+
progress.lessons_completed += 1
|
| 254 |
+
progress.last_activity = datetime.now().strftime("%Y-%m-%d %H:%M")
|
| 255 |
+
|
| 256 |
+
if quiz_score is not None:
|
| 257 |
+
progress.add_quiz_score(quiz_score)
|
| 258 |
+
|
| 259 |
+
return progress
|
| 260 |
+
|
| 261 |
+
def get_recommendation(self, progress: UserProgress) -> str:
|
| 262 |
+
"""Generate learning recommendations based on progress"""
|
| 263 |
+
avg_score = progress.get_average_score()
|
| 264 |
+
|
| 265 |
+
if progress.lessons_completed == 0:
|
| 266 |
+
return "π― Ready to start your learning journey! Begin with your first lesson."
|
| 267 |
+
elif avg_score >= 0.8:
|
| 268 |
+
return f"π Excellent work! You're mastering {progress.skill}. Ready for the next challenge?"
|
| 269 |
+
elif avg_score >= 0.6:
|
| 270 |
+
return f"π Good progress! Keep practicing {progress.skill} to build confidence."
|
| 271 |
+
else:
|
| 272 |
+
return f"πͺ Don't give up! Review the concepts and try again. Practice makes perfect!"
|
| 273 |
+
|
| 274 |
+
class AgenticSkillBuilder:
|
| 275 |
+
"""Main orchestrator for the agentic skill building platform"""
|
| 276 |
+
|
| 277 |
+
def __init__(self):
|
| 278 |
+
self.lesson_agent = LessonAgent(client)
|
| 279 |
+
self.quiz_agent = QuizAgent(client)
|
| 280 |
+
self.progress_agent = ProgressAgent()
|
| 281 |
+
self.current_lesson: Optional[Lesson] = None
|
| 282 |
+
self.current_quiz: Optional[Quiz] = None
|
| 283 |
+
self.current_user = "demo_user" # In a real app, this would be from authentication
|
| 284 |
+
|
| 285 |
+
# Predefined skills
|
| 286 |
+
self.predefined_skills = [
|
| 287 |
+
"Python Programming", "Spanish Language", "Public Speaking",
|
| 288 |
+
"Data Science", "Machine Learning", "JavaScript", "Project Management",
|
| 289 |
+
"Digital Marketing", "Creative Writing", "Photography"
|
| 290 |
+
]
|
| 291 |
+
|
| 292 |
+
async def start_lesson(self, skill: str) -> Tuple[str, str, str]:
|
| 293 |
+
"""Start a new lesson for the selected skill"""
|
| 294 |
+
try:
|
| 295 |
+
progress = self.progress_agent.get_user_progress(self.current_user, skill)
|
| 296 |
+
|
| 297 |
+
# Get list of previous lesson titles for context
|
| 298 |
+
previous_lessons = [] # In a real app, you'd store this
|
| 299 |
+
|
| 300 |
+
self.current_lesson = await self.lesson_agent.generate_lesson(
|
| 301 |
+
skill, progress.current_difficulty, previous_lessons
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
lesson_content = f"""
|
| 305 |
+
# π {self.current_lesson.title}
|
| 306 |
+
|
| 307 |
+
**Skill:** {self.current_lesson.skill} | **Level:** {self.current_lesson.difficulty.title()} | **Duration:** ~{self.current_lesson.duration_minutes} min
|
| 308 |
+
|
| 309 |
+
{self.current_lesson.content}
|
| 310 |
+
|
| 311 |
+
### π Key Concepts:
|
| 312 |
+
{chr(10).join([f"β’ {concept}" for concept in self.current_lesson.key_concepts])}
|
| 313 |
+
"""
|
| 314 |
+
|
| 315 |
+
return lesson_content, "β
Complete Lesson", ""
|
| 316 |
+
|
| 317 |
+
except Exception as e:
|
| 318 |
+
logger.error(f"Error starting lesson: {e}")
|
| 319 |
+
return f"β Error generating lesson: {str(e)}", "Try Again", ""
|
| 320 |
+
|
| 321 |
+
async def complete_lesson_and_start_quiz(self) -> Tuple[str, str, str]:
|
| 322 |
+
"""Mark lesson as complete and start the quiz"""
|
| 323 |
+
if not self.current_lesson:
|
| 324 |
+
return "β οΈ No active lesson to complete.", "", ""
|
| 325 |
+
|
| 326 |
+
try:
|
| 327 |
+
# Update progress
|
| 328 |
+
progress = self.progress_agent.update_progress(
|
| 329 |
+
self.current_user, self.current_lesson.skill, lesson_completed=True
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
+
# Generate quiz
|
| 333 |
+
self.current_quiz = await self.quiz_agent.generate_quiz(self.current_lesson, progress)
|
| 334 |
+
|
| 335 |
+
quiz_content = f"""
|
| 336 |
+
# π§ Quiz: {self.current_lesson.title}
|
| 337 |
+
|
| 338 |
+
Test your understanding of the lesson. Answer all questions to see your results!
|
| 339 |
+
|
| 340 |
+
"""
|
| 341 |
+
|
| 342 |
+
# Add questions to the content
|
| 343 |
+
for i, q in enumerate(self.current_quiz.questions, 1):
|
| 344 |
+
quiz_content += f"\n**Question {i}:** {q['question']}\n"
|
| 345 |
+
if q['type'] == 'multiple_choice':
|
| 346 |
+
quiz_content += f"Options: {', '.join(q['options'])}\n"
|
| 347 |
+
elif q['type'] == 'true_false':
|
| 348 |
+
quiz_content += "Answer: True or False\n"
|
| 349 |
+
|
| 350 |
+
return quiz_content, "π Submit Quiz", ""
|
| 351 |
+
|
| 352 |
+
except Exception as e:
|
| 353 |
+
logger.error(f"Error generating quiz: {e}")
|
| 354 |
+
return f"β Error generating quiz: {str(e)}", "", ""
|
| 355 |
+
|
| 356 |
+
def submit_quiz(self, *answers) -> Tuple[str, str, str]:
|
| 357 |
+
"""Process quiz submission and show results"""
|
| 358 |
+
if not self.current_quiz:
|
| 359 |
+
return "β οΈ No active quiz to submit.", "", ""
|
| 360 |
+
|
| 361 |
+
try:
|
| 362 |
+
correct_answers = 0
|
| 363 |
+
total_questions = len(self.current_quiz.questions)
|
| 364 |
+
results = []
|
| 365 |
+
|
| 366 |
+
for i, (question, answer) in enumerate(zip(self.current_quiz.questions, answers)):
|
| 367 |
+
if answer is None or answer == "":
|
| 368 |
+
continue
|
| 369 |
+
|
| 370 |
+
is_correct = False
|
| 371 |
+
correct_answer = question['correct_answer']
|
| 372 |
+
|
| 373 |
+
if question['type'] == 'multiple_choice':
|
| 374 |
+
is_correct = answer.strip().upper() == str(correct_answer).upper()
|
| 375 |
+
elif question['type'] == 'true_false':
|
| 376 |
+
is_correct = answer.lower() == str(correct_answer).lower()
|
| 377 |
+
|
| 378 |
+
if is_correct:
|
| 379 |
+
correct_answers += 1
|
| 380 |
+
|
| 381 |
+
results.append({
|
| 382 |
+
'question': question['question'],
|
| 383 |
+
'your_answer': answer,
|
| 384 |
+
'correct_answer': correct_answer,
|
| 385 |
+
'is_correct': is_correct,
|
| 386 |
+
'explanation': question.get('explanation', '')
|
| 387 |
+
})
|
| 388 |
+
|
| 389 |
+
score = correct_answers / total_questions if total_questions > 0 else 0
|
| 390 |
+
|
| 391 |
+
# Update progress with quiz score
|
| 392 |
+
progress = self.progress_agent.update_progress(
|
| 393 |
+
self.current_user, self.current_lesson.skill, quiz_score=score
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
# Generate results content
|
| 397 |
+
results_content = f"""
|
| 398 |
+
# π― Quiz Results
|
| 399 |
+
|
| 400 |
+
**Score:** {correct_answers}/{total_questions} ({score:.1%})
|
| 401 |
+
|
| 402 |
+
**Performance:** {'π Excellent!' if score >= 0.8 else 'π Good work!' if score >= 0.6 else 'πͺ Keep practicing!'}
|
| 403 |
+
|
| 404 |
+
### Detailed Results:
|
| 405 |
+
"""
|
| 406 |
+
|
| 407 |
+
for i, result in enumerate(results, 1):
|
| 408 |
+
status = "β
" if result['is_correct'] else "β"
|
| 409 |
+
results_content += f"""
|
| 410 |
+
**Q{i}:** {result['question']}
|
| 411 |
+
{status} Your answer: {result['your_answer']}
|
| 412 |
+
Correct answer: {result['correct_answer']}
|
| 413 |
+
{result['explanation']}
|
| 414 |
+
|
| 415 |
+
"""
|
| 416 |
+
|
| 417 |
+
# Add progress and recommendations
|
| 418 |
+
recommendation = self.progress_agent.get_recommendation(progress)
|
| 419 |
+
|
| 420 |
+
results_content += f"""
|
| 421 |
+
### π Your Progress
|
| 422 |
+
- **Lessons completed:** {progress.lessons_completed}
|
| 423 |
+
- **Average score:** {progress.get_average_score():.1%}
|
| 424 |
+
- **Current level:** {progress.current_difficulty.title()}
|
| 425 |
+
|
| 426 |
+
### π― Recommendation
|
| 427 |
+
{recommendation}
|
| 428 |
+
"""
|
| 429 |
+
|
| 430 |
+
return results_content, "π Start New Lesson", ""
|
| 431 |
+
|
| 432 |
+
except Exception as e:
|
| 433 |
+
logger.error(f"Error processing quiz: {e}")
|
| 434 |
+
return f"β Error processing quiz: {str(e)}", "", ""
|
| 435 |
+
|
| 436 |
+
# Initialize the main application
|
| 437 |
+
app = AgenticSkillBuilder()
|
| 438 |
+
|
| 439 |
+
def create_interface():
|
| 440 |
+
"""Create the Gradio interface"""
|
| 441 |
+
|
| 442 |
+
with gr.Blocks(
|
| 443 |
+
title="Agentic Skill Builder",
|
| 444 |
+
theme=gr.themes.Soft(),
|
| 445 |
+
css="""
|
| 446 |
+
.gradio-container {
|
| 447 |
+
max-width: 800px !important;
|
| 448 |
+
margin: auto !important;
|
| 449 |
+
}
|
| 450 |
+
"""
|
| 451 |
+
) as demo:
|
| 452 |
+
|
| 453 |
+
# Header
|
| 454 |
+
gr.Markdown("""
|
| 455 |
+
# π Agentic Skill Builder
|
| 456 |
+
### AI-Powered Microlearning Platform
|
| 457 |
+
|
| 458 |
+
Learn new skills through bite-sized lessons and adaptive quizzes powered by Azure OpenAI!
|
| 459 |
+
""")
|
| 460 |
+
|
| 461 |
+
# State variables
|
| 462 |
+
current_skill = gr.State("")
|
| 463 |
+
with gr.Tab("π― Start Learning"):
|
| 464 |
+
gr.Markdown("### Choose a skill to begin your microlearning journey")
|
| 465 |
+
|
| 466 |
+
with gr.Row():
|
| 467 |
+
with gr.Column():
|
| 468 |
+
skill_dropdown = gr.Dropdown(
|
| 469 |
+
choices=app.predefined_skills,
|
| 470 |
+
label="π Select a Skill",
|
| 471 |
+
info="Choose from popular skills..."
|
| 472 |
+
)
|
| 473 |
+
custom_skill = gr.Textbox(
|
| 474 |
+
label="βοΈ Or enter a custom skill",
|
| 475 |
+
info="e.g., Cooking, Guitar, Time Management..."
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
start_btn = gr.Button("π Start Learning", variant="primary", size="lg")
|
| 479 |
+
|
| 480 |
+
# Lesson content area
|
| 481 |
+
lesson_output = gr.Markdown(visible=False)
|
| 482 |
+
lesson_btn = gr.Button("Complete Lesson", visible=False)
|
| 483 |
+
|
| 484 |
+
# Quiz area
|
| 485 |
+
quiz_output = gr.Markdown(visible=False)
|
| 486 |
+
|
| 487 |
+
# Dynamic quiz inputs (will be created based on quiz content)
|
| 488 |
+
quiz_inputs = []
|
| 489 |
+
for i in range(5): # Max 5 questions
|
| 490 |
+
quiz_inputs.append(gr.Textbox(label=f"Answer {i+1}", visible=False))
|
| 491 |
+
|
| 492 |
+
quiz_submit_btn = gr.Button("Submit Quiz", visible=False)
|
| 493 |
+
|
| 494 |
+
# Results area
|
| 495 |
+
results_output = gr.Markdown(visible=False)
|
| 496 |
+
restart_btn = gr.Button("Start New Lesson", visible=False)
|
| 497 |
+
|
| 498 |
+
with gr.Tab("π Progress Dashboard"):
|
| 499 |
+
gr.Markdown("### Your Learning Analytics")
|
| 500 |
+
|
| 501 |
+
progress_display = gr.Markdown("""
|
| 502 |
+
**Welcome to your progress dashboard!**
|
| 503 |
+
|
| 504 |
+
Complete some lessons to see your learning analytics here.
|
| 505 |
+
""")
|
| 506 |
+
|
| 507 |
+
refresh_progress_btn = gr.Button("π Refresh Progress")
|
| 508 |
+
|
| 509 |
+
with gr.Tab("π MCP Endpoints"):
|
| 510 |
+
gr.Markdown("""
|
| 511 |
+
### Model Context Protocol Integration
|
| 512 |
+
|
| 513 |
+
This application exposes MCP endpoints for integration with external agents:
|
| 514 |
+
|
| 515 |
+
- **GET /lesson/{skill}** - Fetch next lesson for a skill
|
| 516 |
+
- **GET /progress/{user_id}** - Get user progress data
|
| 517 |
+
- **POST /quiz/submit** - Submit quiz results
|
| 518 |
+
|
| 519 |
+
*Coming soon: Full MCP server implementation*
|
| 520 |
+
""")
|
| 521 |
+
# Event handlers
|
| 522 |
+
async def handle_start_learning(skill_choice, custom_skill_input):
|
| 523 |
+
skill = custom_skill_input.strip() if custom_skill_input.strip() else skill_choice
|
| 524 |
+
if not skill:
|
| 525 |
+
return [
|
| 526 |
+
gr.update(value="β οΈ Please select or enter a skill to continue."),
|
| 527 |
+
gr.update(visible=False),
|
| 528 |
+
gr.update(visible=False),
|
| 529 |
+
skill
|
| 530 |
+
] + [gr.update(visible=False, value="") for _ in range(5)]
|
| 531 |
+
|
| 532 |
+
lesson_content, btn_text, _ = await app.start_lesson(skill)
|
| 533 |
+
|
| 534 |
+
return [
|
| 535 |
+
gr.update(value=lesson_content),
|
| 536 |
+
gr.update(value=btn_text, visible=True),
|
| 537 |
+
gr.update(visible=False),
|
| 538 |
+
skill
|
| 539 |
+
] + [gr.update(visible=False, value="") for _ in range(5)]
|
| 540 |
+
|
| 541 |
+
async def handle_complete_lesson():
|
| 542 |
+
quiz_content, btn_text, _ = await app.complete_lesson_and_start_quiz()
|
| 543 |
+
|
| 544 |
+
# Show quiz inputs based on number of questions
|
| 545 |
+
quiz_updates = []
|
| 546 |
+
if app.current_quiz:
|
| 547 |
+
for i, question in enumerate(app.current_quiz.questions):
|
| 548 |
+
if i < len(quiz_inputs):
|
| 549 |
+
label = f"Q{i+1}: {question['question'][:50]}..."
|
| 550 |
+
quiz_updates.append(gr.update(label=label, visible=True))
|
| 551 |
+
else:
|
| 552 |
+
quiz_updates.append(gr.update(visible=False))
|
| 553 |
+
# Hide remaining inputs
|
| 554 |
+
for i in range(len(app.current_quiz.questions), len(quiz_inputs)):
|
| 555 |
+
quiz_updates.append(gr.update(visible=False))
|
| 556 |
+
else:
|
| 557 |
+
quiz_updates = [gr.update(visible=False) for _ in range(len(quiz_inputs))]
|
| 558 |
+
|
| 559 |
+
return [
|
| 560 |
+
gr.update(visible=False),
|
| 561 |
+
gr.update(value=quiz_content, visible=True),
|
| 562 |
+
gr.update(value=btn_text, visible=True),
|
| 563 |
+
gr.update(visible=False)
|
| 564 |
+
] + quiz_updates
|
| 565 |
+
|
| 566 |
+
def handle_submit_quiz(*answers):
|
| 567 |
+
# Filter out None values and empty strings
|
| 568 |
+
valid_answers = [ans for ans in answers if ans is not None and ans != ""]
|
| 569 |
+
|
| 570 |
+
results_content, btn_text, _ = app.submit_quiz(*valid_answers)
|
| 571 |
+
|
| 572 |
+
return [
|
| 573 |
+
gr.update(visible=False),
|
| 574 |
+
gr.update(value=results_content, visible=True),
|
| 575 |
+
gr.update(value=btn_text, visible=True),
|
| 576 |
+
gr.update(visible=False)
|
| 577 |
+
] + [gr.update(visible=False) for _ in range(len(quiz_inputs))]
|
| 578 |
+
|
| 579 |
+
def handle_restart():
|
| 580 |
+
return [
|
| 581 |
+
gr.update(visible=False),
|
| 582 |
+
gr.update(visible=False),
|
| 583 |
+
gr.update(visible=False),
|
| 584 |
+
gr.update(visible=False),
|
| 585 |
+
gr.update(visible=False),
|
| 586 |
+
""
|
| 587 |
+
] + [gr.update(visible=False, value="") for _ in range(len(quiz_inputs))]
|
| 588 |
+
|
| 589 |
+
def update_progress_display():
|
| 590 |
+
if not app.progress_agent.user_data:
|
| 591 |
+
return "**No learning data yet.** Complete some lessons to see your progress!"
|
| 592 |
+
|
| 593 |
+
progress_content = "# π Your Learning Progress\n\n"
|
| 594 |
+
|
| 595 |
+
for key, progress in app.progress_agent.user_data.items():
|
| 596 |
+
progress_content += f"""
|
| 597 |
+
**Skill:** {progress.skill}
|
| 598 |
+
- Lessons completed: {progress.lessons_completed}
|
| 599 |
+
- Average quiz score: {progress.get_average_score():.1%}
|
| 600 |
+
- Current difficulty: {progress.current_difficulty.title()}
|
| 601 |
+
- Last activity: {progress.last_activity or 'Never'}
|
| 602 |
+
|
| 603 |
+
"""
|
| 604 |
+
return progress_content
|
| 605 |
+
|
| 606 |
+
# Wire up the events
|
| 607 |
+
start_btn.click(
|
| 608 |
+
handle_start_learning,
|
| 609 |
+
inputs=[skill_dropdown, custom_skill],
|
| 610 |
+
outputs=[lesson_output, lesson_btn, quiz_output, current_skill] + quiz_inputs[:5]
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
lesson_btn.click(
|
| 614 |
+
handle_complete_lesson,
|
| 615 |
+
outputs=[lesson_btn, quiz_output, quiz_submit_btn, results_output] + quiz_inputs
|
| 616 |
+
)
|
| 617 |
+
|
| 618 |
+
quiz_submit_btn.click(
|
| 619 |
+
handle_submit_quiz,
|
| 620 |
+
inputs=quiz_inputs,
|
| 621 |
+
outputs=[quiz_submit_btn, results_output, restart_btn, quiz_output] + quiz_inputs
|
| 622 |
+
)
|
| 623 |
+
|
| 624 |
+
restart_btn.click(
|
| 625 |
+
handle_restart,
|
| 626 |
+
outputs=[lesson_output, quiz_output, results_output, lesson_btn, restart_btn, current_skill] + quiz_inputs
|
| 627 |
+
)
|
| 628 |
+
|
| 629 |
+
refresh_progress_btn.click(
|
| 630 |
+
update_progress_display,
|
| 631 |
+
outputs=[progress_display]
|
| 632 |
+
)
|
| 633 |
+
|
| 634 |
+
return demo
|
| 635 |
+
|
| 636 |
+
def main():
|
| 637 |
+
"""Main application entry point"""
|
| 638 |
+
demo = create_interface()
|
| 639 |
+
demo.launch(
|
| 640 |
+
server_name="0.0.0.0",
|
| 641 |
+
server_port=7860,
|
| 642 |
+
share=False,
|
| 643 |
+
show_error=True
|
| 644 |
+
)
|
| 645 |
+
|
| 646 |
+
if __name__ == "__main__":
|
| 647 |
+
main()
|
config.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Configuration module for Agentic Skill Builder
|
| 3 |
+
Handles environment variables, logging, and application settings
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import logging
|
| 8 |
+
from typing import Optional
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
|
| 11 |
+
@dataclass
|
| 12 |
+
class AzureOpenAIConfig:
|
| 13 |
+
"""Configuration for Azure OpenAI service"""
|
| 14 |
+
endpoint: str
|
| 15 |
+
api_key: str
|
| 16 |
+
api_version: str
|
| 17 |
+
llm_deployment: str
|
| 18 |
+
llm_model: str
|
| 19 |
+
embeddings_deployment: str
|
| 20 |
+
embeddings_model: str
|
| 21 |
+
|
| 22 |
+
@classmethod
|
| 23 |
+
def from_env(cls) -> 'AzureOpenAIConfig':
|
| 24 |
+
"""Create configuration from environment variables"""
|
| 25 |
+
return cls(
|
| 26 |
+
endpoint=os.getenv("AZURE_OPENAI_ENDPOINT", "").replace('"', ''),
|
| 27 |
+
api_key=os.getenv("AZURE_OPENAI_KEY", "").replace('"', ''),
|
| 28 |
+
api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-12-01-preview").replace('"', ''),
|
| 29 |
+
llm_deployment=os.getenv("AZURE_OPENAI_LLM_DEPLOYMENT", "gpt-4.1").replace('"', ''),
|
| 30 |
+
llm_model=os.getenv("AZURE_OPENAI_LLM_MODEL", "gpt-4.1").replace('"', ''),
|
| 31 |
+
embeddings_deployment=os.getenv("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT", "text-embedding-3-small").replace('"', ''),
|
| 32 |
+
embeddings_model=os.getenv("AZURE_OPENAI_EMBEDDINGS_MODEL", "text-embedding-3-small").replace('"', '')
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def validate(self) -> bool:
|
| 36 |
+
"""Validate that all required settings are present"""
|
| 37 |
+
required_fields = [self.endpoint, self.api_key, self.llm_deployment]
|
| 38 |
+
return all(field.strip() for field in required_fields)
|
| 39 |
+
|
| 40 |
+
@dataclass
|
| 41 |
+
class AppConfig:
|
| 42 |
+
"""Main application configuration"""
|
| 43 |
+
debug: bool = False
|
| 44 |
+
log_level: str = "INFO"
|
| 45 |
+
gradio_port: int = 7860
|
| 46 |
+
mcp_port: int = 8000
|
| 47 |
+
max_quiz_questions: int = 5
|
| 48 |
+
default_lesson_duration: int = 5
|
| 49 |
+
azure_openai: Optional[AzureOpenAIConfig] = None
|
| 50 |
+
|
| 51 |
+
@classmethod
|
| 52 |
+
def from_env(cls) -> 'AppConfig':
|
| 53 |
+
"""Create configuration from environment variables"""
|
| 54 |
+
return cls(
|
| 55 |
+
debug=os.getenv("DEBUG", "false").lower() == "true",
|
| 56 |
+
log_level=os.getenv("LOG_LEVEL", "INFO").upper(),
|
| 57 |
+
gradio_port=int(os.getenv("GRADIO_PORT", "7860")),
|
| 58 |
+
mcp_port=int(os.getenv("MCP_PORT", "8000")),
|
| 59 |
+
max_quiz_questions=int(os.getenv("MAX_QUIZ_QUESTIONS", "5")),
|
| 60 |
+
default_lesson_duration=int(os.getenv("DEFAULT_LESSON_DURATION", "5")),
|
| 61 |
+
azure_openai=AzureOpenAIConfig.from_env()
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
def setup_logging(config: AppConfig):
|
| 65 |
+
"""Setup application logging"""
|
| 66 |
+
logging.basicConfig(
|
| 67 |
+
level=getattr(logging, config.log_level),
|
| 68 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 69 |
+
handlers=[
|
| 70 |
+
logging.StreamHandler(),
|
| 71 |
+
logging.FileHandler('agentic_skill_builder.log')
|
| 72 |
+
]
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
def get_config() -> AppConfig:
|
| 76 |
+
"""Get application configuration"""
|
| 77 |
+
config = AppConfig.from_env()
|
| 78 |
+
|
| 79 |
+
# Validate Azure OpenAI configuration
|
| 80 |
+
if not config.azure_openai or not config.azure_openai.validate():
|
| 81 |
+
raise ValueError(
|
| 82 |
+
"Azure OpenAI configuration is incomplete. "
|
| 83 |
+
"Please check your .env file for AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_KEY, and AZURE_OPENAI_LLM_DEPLOYMENT"
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
return config
|
demo_video_script.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# π¬ Demo Video Script - Agentic Skill Builder MCP Server
|
| 2 |
+
|
| 3 |
+
## Video Duration: 3-5 minutes
|
| 4 |
+
## Target Audience: Hackathon judges and MCP developers
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## π― **Opening (0:00 - 0:30)**
|
| 9 |
+
|
| 10 |
+
**Visual:** Screen showing the Gradio interface running on localhost:7860
|
| 11 |
+
**Narration:**
|
| 12 |
+
> "Welcome to the Agentic Skill Builder - our submission for the Gradio Agents & MCP Hackathon 2025, Track 1: MCP Server/Tool. This is a unique AI-powered microlearning platform that serves both as a beautiful Gradio interface AND a fully functional MCP server."
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
## π **MCP Server Demonstration (0:30 - 2:00)**
|
| 17 |
+
|
| 18 |
+
### Part 1: Server Status & Endpoints (0:30 - 1:00)
|
| 19 |
+
**Visual:** Browser showing http://localhost:8001/
|
| 20 |
+
**Narration:**
|
| 21 |
+
> "First, let me show you our MCP server running on port 8001. This endpoint provides metadata about our hackathon submission."
|
| 22 |
+
|
| 23 |
+
**Action:** Show JSON response with hackathon information
|
| 24 |
+
|
| 25 |
+
**Visual:** Terminal/Postman showing MCP endpoints
|
| 26 |
+
**Narration:**
|
| 27 |
+
> "Our MCP server exposes four key endpoints:
|
| 28 |
+
> - GET /mcp/skills - Lists available learning skills
|
| 29 |
+
> - POST /mcp/lesson/generate - Creates personalized lessons
|
| 30 |
+
> - GET /mcp/progress/{user_id} - Tracks learning progress
|
| 31 |
+
> - POST /mcp/quiz/submit - Processes quiz submissions"
|
| 32 |
+
|
| 33 |
+
### Part 2: Live MCP Endpoint Testing (1:00 - 2:00)
|
| 34 |
+
**Visual:** Testing each MCP endpoint with curl or PowerShell
|
| 35 |
+
**Narration:**
|
| 36 |
+
> "Let me demonstrate these endpoints in action. First, getting available skills..."
|
| 37 |
+
|
| 38 |
+
**Action:** Show GET /mcp/skills response
|
| 39 |
+
```powershell
|
| 40 |
+
curl http://localhost:8001/mcp/skills
|
| 41 |
+
```
|
| 42 |
+
**Expected Response:**
|
| 43 |
+
```json
|
| 44 |
+
{
|
| 45 |
+
"predefined_skills": ["Python Programming", "Spanish Language", "Public Speaking", "Data Science", "Machine Learning", "JavaScript", "Project Management", "Digital Marketing", "Creative Writing", "Photography"]
|
| 46 |
+
}
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
> "Now generating a personalized lesson for Python Programming..."
|
| 50 |
+
|
| 51 |
+
**Action:** Show POST /mcp/lesson/generate with request body and response
|
| 52 |
+
```powershell
|
| 53 |
+
curl -X POST http://localhost:8001/mcp/lesson/generate `
|
| 54 |
+
-H "Content-Type: application/json" `
|
| 55 |
+
-d '{
|
| 56 |
+
"skill": "Python Programming",
|
| 57 |
+
"level": "beginner",
|
| 58 |
+
"user_context": "I want to learn Python for data analysis"
|
| 59 |
+
}'
|
| 60 |
+
```
|
| 61 |
+
**Expected Response:**
|
| 62 |
+
```json
|
| 63 |
+
{
|
| 64 |
+
"lesson_id": "lesson_12345",
|
| 65 |
+
"skill": "Python Programming",
|
| 66 |
+
"title": "Introduction to Python for Data Analysis",
|
| 67 |
+
"content": "Python is a powerful programming language...",
|
| 68 |
+
"difficulty": "beginner",
|
| 69 |
+
"estimated_time": "15 minutes",
|
| 70 |
+
"mcp_server": "Agentic Skill Builder"
|
| 71 |
+
}
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
> "Let's check user progress to see learning analytics..."
|
| 75 |
+
|
| 76 |
+
**Action:** Show GET /mcp/progress/demo_user response
|
| 77 |
+
```powershell
|
| 78 |
+
curl http://localhost:8001/mcp/progress/demo_user
|
| 79 |
+
```
|
| 80 |
+
**Expected Response:**
|
| 81 |
+
```json
|
| 82 |
+
{
|
| 83 |
+
"user_id": "demo_user",
|
| 84 |
+
"skills_progress": {
|
| 85 |
+
"Python Programming": {
|
| 86 |
+
"lessons_completed": 2,
|
| 87 |
+
"quiz_scores": [85, 92],
|
| 88 |
+
"current_level": "intermediate"
|
| 89 |
+
}
|
| 90 |
+
},
|
| 91 |
+
"total_skills_learning": 1,
|
| 92 |
+
"mcp_server": "Agentic Skill Builder",
|
| 93 |
+
"timestamp": "2025-06-07T04:48:36.691517"
|
| 94 |
+
}
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
> "Finally, let's submit a quiz answer to show the interactive capabilities..."
|
| 98 |
+
|
| 99 |
+
**Action:** Show POST /mcp/quiz/submit with request body and response
|
| 100 |
+
```powershell
|
| 101 |
+
curl -X POST http://localhost:8001/mcp/quiz/submit `
|
| 102 |
+
-H "Content-Type: application/json" `
|
| 103 |
+
-d '{
|
| 104 |
+
"user_id": "demo_user",
|
| 105 |
+
"quiz_id": "quiz_python_001",
|
| 106 |
+
"answers": ["list", "dictionary", "tuple"],
|
| 107 |
+
"skill": "Python Programming"
|
| 108 |
+
}'
|
| 109 |
+
```
|
| 110 |
+
**Expected Response:**
|
| 111 |
+
```json
|
| 112 |
+
{
|
| 113 |
+
"quiz_id": "quiz_python_001",
|
| 114 |
+
"user_id": "demo_user",
|
| 115 |
+
"score": 85,
|
| 116 |
+
"feedback": "Great job! You correctly identified Python data structures.",
|
| 117 |
+
"passed": true,
|
| 118 |
+
"mcp_server": "Agentic Skill Builder"
|
| 119 |
+
}
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
---
|
| 123 |
+
|
| 124 |
+
## π¨ **Gradio Interface Demo (2:00 - 3:30)**
|
| 125 |
+
|
| 126 |
+
### Part 1: Learning Flow (2:00 - 3:00)
|
| 127 |
+
**Visual:** Gradio interface at localhost:7860
|
| 128 |
+
**Narration:**
|
| 129 |
+
> "Now let's see the beautiful Gradio interface in action. The same AI agents that power our MCP endpoints create this seamless learning experience."
|
| 130 |
+
|
| 131 |
+
**Action:**
|
| 132 |
+
1. Select "Data Science" skill
|
| 133 |
+
2. Click "Start Learning"
|
| 134 |
+
3. Show generated lesson content
|
| 135 |
+
4. Complete lesson and start quiz
|
| 136 |
+
5. Answer quiz questions
|
| 137 |
+
6. Show results and progress
|
| 138 |
+
|
| 139 |
+
### Part 2: MCP Testing Interface (3:00 - 3:30)
|
| 140 |
+
**Visual:** Built-in MCP endpoint testing section in Gradio
|
| 141 |
+
**Narration:**
|
| 142 |
+
> "What makes this special is that we've built MCP endpoint testing directly into our Gradio interface. You can test all our MCP endpoints without leaving the app."
|
| 143 |
+
|
| 144 |
+
**Action:** Show the MCP endpoint testing interface working
|
| 145 |
+
|
| 146 |
+
---
|
| 147 |
+
|
| 148 |
+
## π **Hackathon Highlights (3:30 - 4:30)**
|
| 149 |
+
|
| 150 |
+
**Visual:** Split screen showing both Gradio UI and MCP endpoints
|
| 151 |
+
**Narration:**
|
| 152 |
+
> "This submission perfectly demonstrates the agentic architecture we've built:
|
| 153 |
+
> - Three specialized AI agents working together
|
| 154 |
+
> - Lesson Agent generates personalized content
|
| 155 |
+
> - Quiz Agent creates adaptive assessments
|
| 156 |
+
> - Progress Agent tracks learning analytics
|
| 157 |
+
> - All coordinated by our main orchestrator"
|
| 158 |
+
|
| 159 |
+
**Visual:** Show the architecture diagram or code structure
|
| 160 |
+
**Narration:**
|
| 161 |
+
> "The same agents power both the beautiful user interface AND the MCP protocol endpoints, making our platform ready for integration with Claude Desktop, Cursor, or any MCP client."
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
## π **Call to Action (4:30 - 5:00)**
|
| 166 |
+
|
| 167 |
+
**Visual:** README.md showing deployment instructions
|
| 168 |
+
**Narration:**
|
| 169 |
+
> "You can deploy this immediately to Hugging Face Spaces using our space_app.py file, or run it locally following our comprehensive documentation. This represents the future of AI-powered education - agentic, interoperable, and ready for the Model Context Protocol ecosystem."
|
| 170 |
+
|
| 171 |
+
**Visual:** Final shot of both servers running simultaneously
|
| 172 |
+
**Narration:**
|
| 173 |
+
> "Thank you for watching our Agentic Skill Builder demo. We're excited to contribute to the MCP ecosystem and the future of AI agents working together!"
|
| 174 |
+
|
| 175 |
+
---
|
| 176 |
+
|
| 177 |
+
## π **Recording Checklist**
|
| 178 |
+
|
| 179 |
+
### Before Recording:
|
| 180 |
+
- [ ] Ensure both servers are running (Gradio on 7860, MCP on 8001)
|
| 181 |
+
- [ ] Prepare browser tabs for all endpoints
|
| 182 |
+
- [ ] Have curl commands or Postman collection ready
|
| 183 |
+
- [ ] Test the complete learning flow works
|
| 184 |
+
- [ ] Check audio/screen recording quality
|
| 185 |
+
|
| 186 |
+
### During Recording:
|
| 187 |
+
- [ ] Speak clearly and at moderate pace
|
| 188 |
+
- [ ] Show actual JSON responses from MCP endpoints
|
| 189 |
+
- [ ] Demonstrate real-time lesson generation
|
| 190 |
+
- [ ] Highlight the dual-purpose architecture
|
| 191 |
+
- [ ] Keep within 5-minute time limit
|
| 192 |
+
|
| 193 |
+
### After Recording:
|
| 194 |
+
- [ ] Upload to YouTube/Vimeo
|
| 195 |
+
- [ ] Update README.md with video link
|
| 196 |
+
- [ ] Add video link to HF Spaces README
|
| 197 |
+
- [ ] Verify video is publicly accessible
|
| 198 |
+
|
| 199 |
+
---
|
| 200 |
+
|
| 201 |
+
## π₯ **Technical Recording Tips**
|
| 202 |
+
|
| 203 |
+
1. **Use OBS Studio or similar** for high-quality screen recording
|
| 204 |
+
2. **Record at 1080p** for clear text visibility
|
| 205 |
+
3. **Use good microphone** for clear narration
|
| 206 |
+
4. **Multiple takes OK** - edit together the best parts
|
| 207 |
+
5. **Add captions** for accessibility
|
| 208 |
+
6. **Include timestamps** in video description
|
| 209 |
+
|
| 210 |
+
---
|
| 211 |
+
|
| 212 |
+
**Ready to showcase the future of agentic learning with MCP integration! π**
|
deployment_guide.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# π Hugging Face Spaces Deployment Guide
|
| 2 |
+
|
| 3 |
+
## π Pre-Deployment Checklist
|
| 4 |
+
|
| 5 |
+
### β
**Files Ready for Upload:**
|
| 6 |
+
- `space_app.py` - Main application file (Gradio + MCP server)
|
| 7 |
+
- `requirements.txt` - HF Spaces optimized dependencies
|
| 8 |
+
- `README_spaces.md` - Spaces configuration (rename to README.md)
|
| 9 |
+
- `config.py` - Configuration utilities
|
| 10 |
+
- `.env` - Environment variables (handle separately)
|
| 11 |
+
|
| 12 |
+
### β
**Configuration Complete:**
|
| 13 |
+
- App file specified: `space_app.py`
|
| 14 |
+
- SDK: `gradio 4.44.0`
|
| 15 |
+
- Tags include: `mcp-server-track`
|
| 16 |
+
- License: MIT
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## π§ **Deployment Steps**
|
| 21 |
+
|
| 22 |
+
### **Step 1: Create Hugging Face Space**
|
| 23 |
+
|
| 24 |
+
1. Go to [Hugging Face Spaces](https://huggingface.co/spaces)
|
| 25 |
+
2. Click "Create new Space"
|
| 26 |
+
3. **Important:** Create under the `Agents-MCP-Hackathon` organization
|
| 27 |
+
4. Space name: `agentic-skill-builder`
|
| 28 |
+
5. Choose "Gradio" as SDK
|
| 29 |
+
6. Set to Public
|
| 30 |
+
7. Click "Create Space"
|
| 31 |
+
|
| 32 |
+
### **Step 2: Upload Files**
|
| 33 |
+
|
| 34 |
+
Upload these files to your new Space:
|
| 35 |
+
|
| 36 |
+
```
|
| 37 |
+
π agentic-skill-builder/
|
| 38 |
+
βββ π README.md (renamed from README_spaces.md)
|
| 39 |
+
βββ π space_app.py
|
| 40 |
+
βββ π requirements.txt
|
| 41 |
+
βββ π config.py
|
| 42 |
+
βββ π .env (create in Spaces settings)
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
### **Step 3: Configure Environment Variables**
|
| 46 |
+
|
| 47 |
+
In your HF Space settings, add these environment variables:
|
| 48 |
+
|
| 49 |
+
```env
|
| 50 |
+
AZURE_OPENAI_ENDPOINT=your-azure-endpoint
|
| 51 |
+
AZURE_OPENAI_KEY=your-api-key
|
| 52 |
+
AZURE_OPENAI_API_VERSION=2024-12-01-preview
|
| 53 |
+
AZURE_OPENAI_LLM_DEPLOYMENT=gpt-4.1
|
| 54 |
+
AZURE_OPENAI_LLM_MODEL=gpt-4.1
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
**β οΈ Important:** Never commit your `.env` file with real credentials to a public repo!
|
| 58 |
+
|
| 59 |
+
### **Step 4: Verify Deployment**
|
| 60 |
+
|
| 61 |
+
Once deployed, your Space should:
|
| 62 |
+
- β
Show the Gradio interface
|
| 63 |
+
- β
Respond to MCP endpoints at `https://your-space-name.hf.space/mcp/skills`
|
| 64 |
+
- β
Generate lessons and quizzes
|
| 65 |
+
- β
Include hackathon branding and MCP testing interface
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
## π **Testing Your Deployed Space**
|
| 70 |
+
|
| 71 |
+
### **Gradio Interface Test:**
|
| 72 |
+
1. Visit your Space URL
|
| 73 |
+
2. Select a skill (e.g., "Python Programming")
|
| 74 |
+
3. Complete the full learning flow
|
| 75 |
+
4. Verify AI generates lessons and quizzes
|
| 76 |
+
|
| 77 |
+
### **MCP Endpoints Test:**
|
| 78 |
+
```bash
|
| 79 |
+
# Test from anywhere on the internet
|
| 80 |
+
curl https://your-space-name.hf.space/mcp/skills
|
| 81 |
+
curl https://your-space-name.hf.space/mcp/progress/test_user
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## π **Post-Deployment Updates**
|
| 87 |
+
|
| 88 |
+
### **Update README.md with:**
|
| 89 |
+
1. **Live demo link:** Replace local URLs with your HF Space URL
|
| 90 |
+
2. **Video link:** Add your demo video URL
|
| 91 |
+
3. **Deployment status:** Confirm it's live and working
|
| 92 |
+
|
| 93 |
+
### **Example Updates:**
|
| 94 |
+
```markdown
|
| 95 |
+
## π¬ Demo Video
|
| 96 |
+
**MCP Server in Action:** [https://youtu.be/your-video-id](https://youtu.be/your-video-id)
|
| 97 |
+
|
| 98 |
+
## π Live Demo
|
| 99 |
+
**Try it now:** [https://huggingface.co/spaces/Agents-MCP-Hackathon/agentic-skill-builder](https://huggingface.co/spaces/Agents-MCP-Hackathon/agentic-skill-builder)
|
| 100 |
+
|
| 101 |
+
## π MCP Endpoints
|
| 102 |
+
Test our live MCP server at: `https://your-space-name.hf.space/mcp/`
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
---
|
| 106 |
+
|
| 107 |
+
## π **Final Hackathon Submission Checklist**
|
| 108 |
+
|
| 109 |
+
- [ ] β
Space deployed under Agents-MCP-Hackathon organization
|
| 110 |
+
- [ ] β
"mcp-server-track" tag in README
|
| 111 |
+
- [ ] β
Demo video uploaded and linked
|
| 112 |
+
- [ ] β
Live MCP endpoints working
|
| 113 |
+
- [ ] β
Gradio interface fully functional
|
| 114 |
+
- [ ] β
All dependencies working in cloud environment
|
| 115 |
+
- [ ] β
Documentation updated with live links
|
| 116 |
+
|
| 117 |
+
---
|
| 118 |
+
|
| 119 |
+
## π¨ **Troubleshooting Common Issues**
|
| 120 |
+
|
| 121 |
+
### **Build Failures:**
|
| 122 |
+
- Check `requirements.txt` for incompatible versions
|
| 123 |
+
- Verify all imports work in `space_app.py`
|
| 124 |
+
- Ensure environment variables are set correctly
|
| 125 |
+
|
| 126 |
+
### **MCP Endpoints Not Working:**
|
| 127 |
+
- Verify FastAPI routes are properly configured
|
| 128 |
+
- Check if app.mount() is correctly set up
|
| 129 |
+
- Test endpoints locally first
|
| 130 |
+
|
| 131 |
+
### **Azure OpenAI Errors:**
|
| 132 |
+
- Confirm environment variables are set in Spaces settings
|
| 133 |
+
- Check Azure OpenAI quota and model availability
|
| 134 |
+
- Verify API key permissions
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
**Ready to deploy! Your agentic skill builder will be live for the world to see! πβ¨**
|
hf-hackathon.code-workspace
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"folders": [
|
| 3 |
+
{
|
| 4 |
+
"path": "."
|
| 5 |
+
}
|
| 6 |
+
],
|
| 7 |
+
"settings": {}
|
| 8 |
+
}
|
mcp_server.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MCP Server Integration for Agentic Skill Builder
|
| 3 |
+
This module provides Model Context Protocol endpoints for external agent integration.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import asyncio
|
| 8 |
+
from typing import Dict, Any, List
|
| 9 |
+
from dataclasses import asdict
|
| 10 |
+
from datetime import datetime
|
| 11 |
+
|
| 12 |
+
from fastapi import FastAPI, HTTPException, Depends
|
| 13 |
+
from pydantic import BaseModel
|
| 14 |
+
import uvicorn
|
| 15 |
+
|
| 16 |
+
# Import from main app
|
| 17 |
+
from app import AgenticSkillBuilder, UserProgress
|
| 18 |
+
|
| 19 |
+
# FastAPI app for MCP endpoints
|
| 20 |
+
mcp_app = FastAPI(
|
| 21 |
+
title="Agentic Skill Builder MCP Server",
|
| 22 |
+
description="Model Context Protocol endpoints for microlearning integration",
|
| 23 |
+
version="1.0.0"
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
# Global app instance
|
| 27 |
+
skill_builder = AgenticSkillBuilder()
|
| 28 |
+
|
| 29 |
+
# Pydantic models for API
|
| 30 |
+
class LessonRequest(BaseModel):
|
| 31 |
+
skill: str
|
| 32 |
+
user_id: str = "default_user"
|
| 33 |
+
difficulty: str = "beginner"
|
| 34 |
+
|
| 35 |
+
class QuizSubmission(BaseModel):
|
| 36 |
+
user_id: str
|
| 37 |
+
skill: str
|
| 38 |
+
lesson_title: str
|
| 39 |
+
answers: List[str]
|
| 40 |
+
|
| 41 |
+
class ProgressResponse(BaseModel):
|
| 42 |
+
user_id: str
|
| 43 |
+
skill: str
|
| 44 |
+
lessons_completed: int
|
| 45 |
+
average_score: float
|
| 46 |
+
current_difficulty: str
|
| 47 |
+
recommendations: str
|
| 48 |
+
|
| 49 |
+
@mcp_app.get("/")
|
| 50 |
+
async def root():
|
| 51 |
+
"""Root endpoint with API information"""
|
| 52 |
+
return {
|
| 53 |
+
"name": "Agentic Skill Builder MCP Server",
|
| 54 |
+
"version": "1.0.0",
|
| 55 |
+
"description": "MCP endpoints for AI-powered microlearning",
|
| 56 |
+
"endpoints": {
|
| 57 |
+
"GET /lesson/{skill}": "Fetch next lesson for a skill",
|
| 58 |
+
"GET /progress/{user_id}": "Get user progress data",
|
| 59 |
+
"POST /quiz/submit": "Submit quiz results",
|
| 60 |
+
"GET /skills": "List available skills"
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
@mcp_app.get("/skills")
|
| 65 |
+
async def get_available_skills():
|
| 66 |
+
"""Get list of available predefined skills"""
|
| 67 |
+
return {
|
| 68 |
+
"predefined_skills": skill_builder.predefined_skills,
|
| 69 |
+
"custom_skills_supported": True,
|
| 70 |
+
"message": "You can also request lessons for any custom skill"
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
@mcp_app.post("/lesson/generate")
|
| 74 |
+
async def generate_lesson(request: LessonRequest):
|
| 75 |
+
"""Generate a new lesson for the specified skill and user"""
|
| 76 |
+
try:
|
| 77 |
+
# Set current user
|
| 78 |
+
skill_builder.current_user = request.user_id
|
| 79 |
+
|
| 80 |
+
# Get user progress
|
| 81 |
+
progress = skill_builder.progress_agent.get_user_progress(
|
| 82 |
+
request.user_id, request.skill
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
# Generate lesson
|
| 86 |
+
lesson = await skill_builder.lesson_agent.generate_lesson(
|
| 87 |
+
skill=request.skill,
|
| 88 |
+
difficulty=request.difficulty or progress.current_difficulty,
|
| 89 |
+
previous_lessons=[] # Could be enhanced to track previous lessons
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
return {
|
| 93 |
+
"lesson": {
|
| 94 |
+
"title": lesson.title,
|
| 95 |
+
"content": lesson.content,
|
| 96 |
+
"skill": lesson.skill,
|
| 97 |
+
"difficulty": lesson.difficulty,
|
| 98 |
+
"duration_minutes": lesson.duration_minutes,
|
| 99 |
+
"key_concepts": lesson.key_concepts
|
| 100 |
+
},
|
| 101 |
+
"user_context": {
|
| 102 |
+
"user_id": request.user_id,
|
| 103 |
+
"current_difficulty": progress.current_difficulty,
|
| 104 |
+
"lessons_completed": progress.lessons_completed
|
| 105 |
+
},
|
| 106 |
+
"timestamp": datetime.now().isoformat()
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
except Exception as e:
|
| 110 |
+
raise HTTPException(status_code=500, detail=f"Error generating lesson: {str(e)}")
|
| 111 |
+
|
| 112 |
+
@mcp_app.get("/progress/{user_id}")
|
| 113 |
+
async def get_user_progress(user_id: str, skill: str = None):
|
| 114 |
+
"""Get user progress data for all skills or a specific skill"""
|
| 115 |
+
try:
|
| 116 |
+
if skill:
|
| 117 |
+
# Get progress for specific skill
|
| 118 |
+
progress = skill_builder.progress_agent.get_user_progress(user_id, skill)
|
| 119 |
+
recommendation = skill_builder.progress_agent.get_recommendation(progress)
|
| 120 |
+
|
| 121 |
+
return ProgressResponse(
|
| 122 |
+
user_id=progress.user_id,
|
| 123 |
+
skill=progress.skill,
|
| 124 |
+
lessons_completed=progress.lessons_completed,
|
| 125 |
+
average_score=progress.get_average_score(),
|
| 126 |
+
current_difficulty=progress.current_difficulty,
|
| 127 |
+
recommendations=recommendation
|
| 128 |
+
)
|
| 129 |
+
else:
|
| 130 |
+
# Get progress for all skills
|
| 131 |
+
user_progress_data = {}
|
| 132 |
+
for key, progress in skill_builder.progress_agent.user_data.items():
|
| 133 |
+
if progress.user_id == user_id:
|
| 134 |
+
user_progress_data[progress.skill] = {
|
| 135 |
+
"lessons_completed": progress.lessons_completed,
|
| 136 |
+
"average_score": progress.get_average_score(),
|
| 137 |
+
"current_difficulty": progress.current_difficulty,
|
| 138 |
+
"quiz_scores": progress.quiz_scores,
|
| 139 |
+
"last_activity": progress.last_activity
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
return {
|
| 143 |
+
"user_id": user_id,
|
| 144 |
+
"skills_progress": user_progress_data,
|
| 145 |
+
"total_skills_learning": len(user_progress_data),
|
| 146 |
+
"timestamp": datetime.now().isoformat()
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
except Exception as e:
|
| 150 |
+
raise HTTPException(status_code=500, detail=f"Error fetching progress: {str(e)}")
|
| 151 |
+
|
| 152 |
+
@mcp_app.post("/quiz/submit")
|
| 153 |
+
async def submit_quiz_results(submission: QuizSubmission):
|
| 154 |
+
"""Submit quiz results and get feedback"""
|
| 155 |
+
try:
|
| 156 |
+
# Set current user
|
| 157 |
+
skill_builder.current_user = submission.user_id
|
| 158 |
+
|
| 159 |
+
# Calculate score (simplified scoring)
|
| 160 |
+
if not skill_builder.current_quiz or len(submission.answers) == 0:
|
| 161 |
+
raise HTTPException(status_code=400, detail="No active quiz or no answers provided")
|
| 162 |
+
|
| 163 |
+
correct_answers = 0
|
| 164 |
+
total_questions = len(skill_builder.current_quiz.questions)
|
| 165 |
+
|
| 166 |
+
for i, (question, answer) in enumerate(zip(skill_builder.current_quiz.questions, submission.answers)):
|
| 167 |
+
if i >= len(submission.answers):
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
correct_answer = str(question['correct_answer']).lower()
|
| 171 |
+
user_answer = answer.lower().strip()
|
| 172 |
+
|
| 173 |
+
if user_answer == correct_answer:
|
| 174 |
+
correct_answers += 1
|
| 175 |
+
|
| 176 |
+
score = correct_answers / total_questions if total_questions > 0 else 0
|
| 177 |
+
|
| 178 |
+
# Update progress
|
| 179 |
+
progress = skill_builder.progress_agent.update_progress(
|
| 180 |
+
submission.user_id, submission.skill, quiz_score=score
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
# Get recommendation
|
| 184 |
+
recommendation = skill_builder.progress_agent.get_recommendation(progress)
|
| 185 |
+
|
| 186 |
+
return {
|
| 187 |
+
"quiz_results": {
|
| 188 |
+
"score": score,
|
| 189 |
+
"correct_answers": correct_answers,
|
| 190 |
+
"total_questions": total_questions,
|
| 191 |
+
"percentage": f"{score:.1%}"
|
| 192 |
+
},
|
| 193 |
+
"updated_progress": {
|
| 194 |
+
"lessons_completed": progress.lessons_completed,
|
| 195 |
+
"average_score": progress.get_average_score(),
|
| 196 |
+
"current_difficulty": progress.current_difficulty
|
| 197 |
+
},
|
| 198 |
+
"recommendation": recommendation,
|
| 199 |
+
"timestamp": datetime.now().isoformat()
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
except Exception as e:
|
| 203 |
+
raise HTTPException(status_code=500, detail=f"Error processing quiz submission: {str(e)}")
|
| 204 |
+
|
| 205 |
+
@mcp_app.post("/quiz/generate")
|
| 206 |
+
async def generate_quiz_for_lesson(lesson_title: str, skill: str, user_id: str = "default_user"):
|
| 207 |
+
"""Generate a quiz for a specific lesson"""
|
| 208 |
+
try:
|
| 209 |
+
# Set current user
|
| 210 |
+
skill_builder.current_user = user_id
|
| 211 |
+
|
| 212 |
+
# Get user progress
|
| 213 |
+
progress = skill_builder.progress_agent.get_user_progress(user_id, skill)
|
| 214 |
+
|
| 215 |
+
# Create a mock lesson object for quiz generation
|
| 216 |
+
from app import Lesson
|
| 217 |
+
mock_lesson = Lesson(
|
| 218 |
+
title=lesson_title,
|
| 219 |
+
content=f"This is content for {lesson_title}",
|
| 220 |
+
skill=skill,
|
| 221 |
+
difficulty=progress.current_difficulty,
|
| 222 |
+
duration_minutes=5,
|
| 223 |
+
key_concepts=["concept1", "concept2"]
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
# Generate quiz
|
| 227 |
+
quiz = await skill_builder.quiz_agent.generate_quiz(mock_lesson, progress)
|
| 228 |
+
|
| 229 |
+
# Store current quiz for submission
|
| 230 |
+
skill_builder.current_quiz = quiz
|
| 231 |
+
|
| 232 |
+
return {
|
| 233 |
+
"quiz": {
|
| 234 |
+
"lesson_title": lesson_title,
|
| 235 |
+
"skill": skill,
|
| 236 |
+
"difficulty": quiz.difficulty,
|
| 237 |
+
"questions": quiz.questions
|
| 238 |
+
},
|
| 239 |
+
"instructions": "Submit answers using the /quiz/submit endpoint",
|
| 240 |
+
"timestamp": datetime.now().isoformat()
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
except Exception as e:
|
| 244 |
+
raise HTTPException(status_code=500, detail=f"Error generating quiz: {str(e)}")
|
| 245 |
+
|
| 246 |
+
@mcp_app.get("/health")
|
| 247 |
+
async def health_check():
|
| 248 |
+
"""Health check endpoint"""
|
| 249 |
+
return {
|
| 250 |
+
"status": "healthy",
|
| 251 |
+
"timestamp": datetime.now().isoformat(),
|
| 252 |
+
"service": "Agentic Skill Builder MCP Server"
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
def run_mcp_server():
|
| 256 |
+
"""Run the MCP server"""
|
| 257 |
+
uvicorn.run(
|
| 258 |
+
"mcp_server:mcp_app",
|
| 259 |
+
host="0.0.0.0",
|
| 260 |
+
port=8000,
|
| 261 |
+
reload=True,
|
| 262 |
+
log_level="info"
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
if __name__ == "__main__":
|
| 266 |
+
print("π Starting Agentic Skill Builder MCP Server...")
|
| 267 |
+
print("π MCP endpoints will be available at http://localhost:8000")
|
| 268 |
+
print("π API documentation at http://localhost:8000/docs")
|
| 269 |
+
run_mcp_server()
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.44.0
|
| 2 |
+
openai>=1.12.0
|
| 3 |
+
python-dotenv>=1.0.0
|
| 4 |
+
pandas>=2.0.0
|
| 5 |
+
uvicorn>=0.24.0
|
| 6 |
+
fastapi>=0.104.0
|
| 7 |
+
pydantic>=2.0.0
|
| 8 |
+
aiohttp>=3.9.0
|
| 9 |
+
requests>=2.31.0
|
run.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Launcher script for Agentic Skill Builder
|
| 3 |
+
Runs both the Gradio interface and MCP server
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import asyncio
|
| 7 |
+
import subprocess
|
| 8 |
+
import sys
|
| 9 |
+
import time
|
| 10 |
+
import threading
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
def run_gradio_app():
|
| 14 |
+
"""Run the main Gradio application"""
|
| 15 |
+
print("π Starting Gradio App...")
|
| 16 |
+
subprocess.run([sys.executable, "app.py"])
|
| 17 |
+
|
| 18 |
+
def run_mcp_server():
|
| 19 |
+
"""Run the MCP server"""
|
| 20 |
+
print("π Starting MCP Server...")
|
| 21 |
+
subprocess.run([sys.executable, "mcp_server.py"])
|
| 22 |
+
|
| 23 |
+
def main():
|
| 24 |
+
"""Main launcher function"""
|
| 25 |
+
print("=" * 60)
|
| 26 |
+
print("π AGENTIC SKILL BUILDER")
|
| 27 |
+
print(" AI-Powered Microlearning Platform")
|
| 28 |
+
print("=" * 60)
|
| 29 |
+
print()
|
| 30 |
+
|
| 31 |
+
# Check if we're in the right directory
|
| 32 |
+
if not Path("app.py").exists():
|
| 33 |
+
print("β Error: app.py not found. Please run this script from the project directory.")
|
| 34 |
+
return
|
| 35 |
+
|
| 36 |
+
print("Choose how to run the application:")
|
| 37 |
+
print("1. Gradio App only (recommended for demo)")
|
| 38 |
+
print("2. MCP Server only")
|
| 39 |
+
print("3. Both Gradio App and MCP Server")
|
| 40 |
+
print()
|
| 41 |
+
|
| 42 |
+
choice = input("Enter your choice (1-3): ").strip()
|
| 43 |
+
|
| 44 |
+
if choice == "1":
|
| 45 |
+
print("\nπ― Starting Gradio App...")
|
| 46 |
+
print("π± Interface will be available at: http://localhost:7860")
|
| 47 |
+
run_gradio_app()
|
| 48 |
+
|
| 49 |
+
elif choice == "2":
|
| 50 |
+
print("\nπ Starting MCP Server...")
|
| 51 |
+
print("π API will be available at: http://localhost:8000")
|
| 52 |
+
print("π API docs at: http://localhost:8000/docs")
|
| 53 |
+
run_mcp_server()
|
| 54 |
+
|
| 55 |
+
elif choice == "3":
|
| 56 |
+
print("\nπ Starting both services...")
|
| 57 |
+
print("π± Gradio App: http://localhost:7860")
|
| 58 |
+
print("π MCP Server: http://localhost:8000")
|
| 59 |
+
print("π API docs: http://localhost:8000/docs")
|
| 60 |
+
print()
|
| 61 |
+
|
| 62 |
+
# Start MCP server in a separate thread
|
| 63 |
+
mcp_thread = threading.Thread(target=run_mcp_server, daemon=True)
|
| 64 |
+
mcp_thread.start()
|
| 65 |
+
|
| 66 |
+
# Give MCP server time to start
|
| 67 |
+
time.sleep(2)
|
| 68 |
+
|
| 69 |
+
# Start Gradio app (this will block)
|
| 70 |
+
run_gradio_app()
|
| 71 |
+
|
| 72 |
+
else:
|
| 73 |
+
print("β Invalid choice. Please run the script again.")
|
| 74 |
+
|
| 75 |
+
if __name__ == "__main__":
|
| 76 |
+
try:
|
| 77 |
+
main()
|
| 78 |
+
except KeyboardInterrupt:
|
| 79 |
+
print("\n\nπ Goodbye! Thanks for using Agentic Skill Builder!")
|
| 80 |
+
except Exception as e:
|
| 81 |
+
print(f"\nβ Error: {e}")
|
| 82 |
+
print("Please check your configuration and try again.")
|
space_app.py
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Agentic Skill Builder - Hackathon Submission
|
| 3 |
+
A unified app.py that serves both Gradio interface and MCP server endpoints
|
| 4 |
+
for the Gradio Agents & MCP Hackathon 2025
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import json
|
| 9 |
+
import asyncio
|
| 10 |
+
import threading
|
| 11 |
+
import time
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
from typing import Dict, List, Optional, Tuple
|
| 14 |
+
from dataclasses import dataclass, asdict
|
| 15 |
+
import logging
|
| 16 |
+
|
| 17 |
+
from dotenv import load_dotenv
|
| 18 |
+
import gradio as gr
|
| 19 |
+
from openai import AzureOpenAI
|
| 20 |
+
import pandas as pd
|
| 21 |
+
from fastapi import FastAPI, HTTPException
|
| 22 |
+
from pydantic import BaseModel
|
| 23 |
+
import uvicorn
|
| 24 |
+
|
| 25 |
+
# Configure logging
|
| 26 |
+
logging.basicConfig(level=logging.INFO)
|
| 27 |
+
logger = logging.getLogger(__name__)
|
| 28 |
+
|
| 29 |
+
# Load environment variables
|
| 30 |
+
load_dotenv()
|
| 31 |
+
|
| 32 |
+
# Azure OpenAI client configuration
|
| 33 |
+
client = AzureOpenAI(
|
| 34 |
+
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT", "").replace('"', ''),
|
| 35 |
+
api_key=os.getenv("AZURE_OPENAI_KEY", "").replace('"', ''),
|
| 36 |
+
api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-12-01-preview").replace('"', ''),
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# Model configurations
|
| 40 |
+
LLM_DEPLOYMENT = os.getenv("AZURE_OPENAI_LLM_DEPLOYMENT", "gpt-4").replace('"', '')
|
| 41 |
+
LLM_MODEL = os.getenv("AZURE_OPENAI_LLM_MODEL", "gpt-4").replace('"', '')
|
| 42 |
+
|
| 43 |
+
# Import all classes from the main app
|
| 44 |
+
from app import (
|
| 45 |
+
UserProgress, Lesson, Quiz, LessonAgent, QuizAgent,
|
| 46 |
+
ProgressAgent, AgenticSkillBuilder
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
# Create global instances
|
| 50 |
+
app_instance = AgenticSkillBuilder()
|
| 51 |
+
|
| 52 |
+
# ===== MCP SERVER INTEGRATION =====
|
| 53 |
+
|
| 54 |
+
# FastAPI app for MCP endpoints
|
| 55 |
+
mcp_app = FastAPI(
|
| 56 |
+
title="Agentic Skill Builder MCP Server",
|
| 57 |
+
description="Model Context Protocol endpoints for microlearning integration - Hackathon 2025",
|
| 58 |
+
version="1.0.0"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# Pydantic models for API
|
| 62 |
+
class LessonRequest(BaseModel):
|
| 63 |
+
skill: str
|
| 64 |
+
user_id: str = "default_user"
|
| 65 |
+
difficulty: str = "beginner"
|
| 66 |
+
|
| 67 |
+
class QuizSubmission(BaseModel):
|
| 68 |
+
user_id: str
|
| 69 |
+
skill: str
|
| 70 |
+
lesson_title: str
|
| 71 |
+
answers: List[str]
|
| 72 |
+
|
| 73 |
+
@mcp_app.get("/")
|
| 74 |
+
async def root():
|
| 75 |
+
"""Root endpoint with hackathon information"""
|
| 76 |
+
return {
|
| 77 |
+
"name": "Agentic Skill Builder MCP Server",
|
| 78 |
+
"version": "1.0.0",
|
| 79 |
+
"hackathon": "Gradio Agents & MCP Hackathon 2025",
|
| 80 |
+
"track": "mcp-server-track",
|
| 81 |
+
"description": "MCP endpoints for AI-powered microlearning",
|
| 82 |
+
"endpoints": {
|
| 83 |
+
"GET /mcp/lesson/generate": "Generate next lesson for a skill",
|
| 84 |
+
"GET /mcp/progress/{user_id}": "Get user progress data",
|
| 85 |
+
"POST /mcp/quiz/submit": "Submit quiz results",
|
| 86 |
+
"GET /mcp/skills": "List available skills"
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
@mcp_app.get("/mcp/skills")
|
| 91 |
+
async def get_available_skills():
|
| 92 |
+
"""Get list of available predefined skills"""
|
| 93 |
+
return {
|
| 94 |
+
"predefined_skills": app_instance.predefined_skills,
|
| 95 |
+
"custom_skills_supported": True,
|
| 96 |
+
"message": "You can also request lessons for any custom skill"
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
@mcp_app.post("/mcp/lesson/generate")
|
| 100 |
+
async def generate_lesson_mcp(request: LessonRequest):
|
| 101 |
+
"""Generate a new lesson via MCP endpoint"""
|
| 102 |
+
try:
|
| 103 |
+
app_instance.current_user = request.user_id
|
| 104 |
+
progress = app_instance.progress_agent.get_user_progress(request.user_id, request.skill)
|
| 105 |
+
|
| 106 |
+
lesson = await app_instance.lesson_agent.generate_lesson(
|
| 107 |
+
skill=request.skill,
|
| 108 |
+
difficulty=request.difficulty or progress.current_difficulty,
|
| 109 |
+
previous_lessons=[]
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
return {
|
| 113 |
+
"lesson": {
|
| 114 |
+
"title": lesson.title,
|
| 115 |
+
"content": lesson.content,
|
| 116 |
+
"skill": lesson.skill,
|
| 117 |
+
"difficulty": lesson.difficulty,
|
| 118 |
+
"duration_minutes": lesson.duration_minutes,
|
| 119 |
+
"key_concepts": lesson.key_concepts
|
| 120 |
+
},
|
| 121 |
+
"user_context": {
|
| 122 |
+
"user_id": request.user_id,
|
| 123 |
+
"current_difficulty": progress.current_difficulty,
|
| 124 |
+
"lessons_completed": progress.lessons_completed
|
| 125 |
+
},
|
| 126 |
+
"mcp_server": "Agentic Skill Builder",
|
| 127 |
+
"timestamp": datetime.now().isoformat()
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
except Exception as e:
|
| 131 |
+
raise HTTPException(status_code=500, detail=f"Error generating lesson: {str(e)}")
|
| 132 |
+
|
| 133 |
+
@mcp_app.get("/mcp/progress/{user_id}")
|
| 134 |
+
async def get_user_progress_mcp(user_id: str, skill: str = None):
|
| 135 |
+
"""Get user progress data via MCP endpoint"""
|
| 136 |
+
try:
|
| 137 |
+
if skill:
|
| 138 |
+
progress = app_instance.progress_agent.get_user_progress(user_id, skill)
|
| 139 |
+
recommendation = app_instance.progress_agent.get_recommendation(progress)
|
| 140 |
+
|
| 141 |
+
return {
|
| 142 |
+
"user_id": progress.user_id,
|
| 143 |
+
"skill": progress.skill,
|
| 144 |
+
"lessons_completed": progress.lessons_completed,
|
| 145 |
+
"average_score": progress.get_average_score(),
|
| 146 |
+
"current_difficulty": progress.current_difficulty,
|
| 147 |
+
"recommendations": recommendation,
|
| 148 |
+
"mcp_server": "Agentic Skill Builder"
|
| 149 |
+
}
|
| 150 |
+
else:
|
| 151 |
+
user_progress_data = {}
|
| 152 |
+
for key, progress in app_instance.progress_agent.user_data.items():
|
| 153 |
+
if progress.user_id == user_id:
|
| 154 |
+
user_progress_data[progress.skill] = {
|
| 155 |
+
"lessons_completed": progress.lessons_completed,
|
| 156 |
+
"average_score": progress.get_average_score(),
|
| 157 |
+
"current_difficulty": progress.current_difficulty,
|
| 158 |
+
"quiz_scores": progress.quiz_scores,
|
| 159 |
+
"last_activity": progress.last_activity
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
return {
|
| 163 |
+
"user_id": user_id,
|
| 164 |
+
"skills_progress": user_progress_data,
|
| 165 |
+
"total_skills_learning": len(user_progress_data),
|
| 166 |
+
"mcp_server": "Agentic Skill Builder",
|
| 167 |
+
"timestamp": datetime.now().isoformat()
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
except Exception as e:
|
| 171 |
+
raise HTTPException(status_code=500, detail=f"Error fetching progress: {str(e)}")
|
| 172 |
+
|
| 173 |
+
@mcp_app.post("/mcp/quiz/submit")
|
| 174 |
+
async def submit_quiz_results_mcp(submission: QuizSubmission):
|
| 175 |
+
"""Submit quiz results via MCP endpoint"""
|
| 176 |
+
try:
|
| 177 |
+
app_instance.current_user = submission.user_id
|
| 178 |
+
|
| 179 |
+
if not app_instance.current_quiz or len(submission.answers) == 0:
|
| 180 |
+
raise HTTPException(status_code=400, detail="No active quiz or no answers provided")
|
| 181 |
+
|
| 182 |
+
correct_answers = 0
|
| 183 |
+
total_questions = len(app_instance.current_quiz.questions)
|
| 184 |
+
|
| 185 |
+
for i, (question, answer) in enumerate(zip(app_instance.current_quiz.questions, submission.answers)):
|
| 186 |
+
if i >= len(submission.answers):
|
| 187 |
+
break
|
| 188 |
+
|
| 189 |
+
correct_answer = str(question['correct_answer']).lower()
|
| 190 |
+
user_answer = answer.lower().strip()
|
| 191 |
+
|
| 192 |
+
if user_answer == correct_answer:
|
| 193 |
+
correct_answers += 1
|
| 194 |
+
|
| 195 |
+
score = correct_answers / total_questions if total_questions > 0 else 0
|
| 196 |
+
|
| 197 |
+
progress = app_instance.progress_agent.update_progress(
|
| 198 |
+
submission.user_id, submission.skill, quiz_score=score
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
recommendation = app_instance.progress_agent.get_recommendation(progress)
|
| 202 |
+
|
| 203 |
+
return {
|
| 204 |
+
"quiz_results": {
|
| 205 |
+
"score": score,
|
| 206 |
+
"correct_answers": correct_answers,
|
| 207 |
+
"total_questions": total_questions,
|
| 208 |
+
"percentage": f"{score:.1%}"
|
| 209 |
+
},
|
| 210 |
+
"updated_progress": {
|
| 211 |
+
"lessons_completed": progress.lessons_completed,
|
| 212 |
+
"average_score": progress.get_average_score(),
|
| 213 |
+
"current_difficulty": progress.current_difficulty
|
| 214 |
+
},
|
| 215 |
+
"recommendation": recommendation,
|
| 216 |
+
"mcp_server": "Agentic Skill Builder",
|
| 217 |
+
"timestamp": datetime.now().isoformat()
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
except Exception as e:
|
| 221 |
+
raise HTTPException(status_code=500, detail=f"Error processing quiz submission: {str(e)}")
|
| 222 |
+
|
| 223 |
+
# ===== GRADIO INTERFACE =====
|
| 224 |
+
|
| 225 |
+
def create_interface():
|
| 226 |
+
"""Create the Gradio interface with enhanced hackathon features"""
|
| 227 |
+
|
| 228 |
+
with gr.Blocks(
|
| 229 |
+
title="Agentic Skill Builder - MCP Hackathon 2025",
|
| 230 |
+
theme=gr.themes.Soft(),
|
| 231 |
+
css="""
|
| 232 |
+
.gradio-container {
|
| 233 |
+
max-width: 900px !important;
|
| 234 |
+
margin: auto !important;
|
| 235 |
+
}
|
| 236 |
+
.hackathon-header {
|
| 237 |
+
background: linear-gradient(90deg, #ff7b7b, #667eea);
|
| 238 |
+
color: white;
|
| 239 |
+
padding: 1rem;
|
| 240 |
+
border-radius: 10px;
|
| 241 |
+
margin-bottom: 1rem;
|
| 242 |
+
}
|
| 243 |
+
"""
|
| 244 |
+
) as demo:
|
| 245 |
+
|
| 246 |
+
# Enhanced Header for Hackathon
|
| 247 |
+
gr.HTML("""
|
| 248 |
+
<div class="hackathon-header">
|
| 249 |
+
<h1>π Agentic Skill Builder</h1>
|
| 250 |
+
<h3>AI-Powered Microlearning with MCP Integration</h3>
|
| 251 |
+
<p><strong>π Gradio Agents & MCP Hackathon 2025 Submission</strong></p>
|
| 252 |
+
<p>Track: MCP Server/Tool β’ Demonstrating Agentic AI Workflows</p>
|
| 253 |
+
</div>
|
| 254 |
+
""")
|
| 255 |
+
|
| 256 |
+
# State variables
|
| 257 |
+
current_skill = gr.State("")
|
| 258 |
+
|
| 259 |
+
with gr.Tab("π― Microlearning Experience"):
|
| 260 |
+
gr.Markdown("""
|
| 261 |
+
### π Start Your AI-Powered Learning Journey
|
| 262 |
+
Choose any skill and let our agentic AI system create personalized lessons and adaptive quizzes for you!
|
| 263 |
+
""")
|
| 264 |
+
|
| 265 |
+
with gr.Row():
|
| 266 |
+
with gr.Column():
|
| 267 |
+
skill_dropdown = gr.Dropdown(
|
| 268 |
+
choices=app_instance.predefined_skills,
|
| 269 |
+
label="π Select a Popular Skill",
|
| 270 |
+
info="Choose from trending skills..."
|
| 271 |
+
)
|
| 272 |
+
custom_skill = gr.Textbox(
|
| 273 |
+
label="βοΈ Or Enter Any Custom Skill",
|
| 274 |
+
info="e.g., Quantum Computing, Meditation, Game Development...",
|
| 275 |
+
placeholder="What would you like to learn today?"
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
start_btn = gr.Button("π Start Learning", variant="primary", size="lg")
|
| 279 |
+
|
| 280 |
+
# Learning content areas
|
| 281 |
+
lesson_output = gr.Markdown(visible=False)
|
| 282 |
+
lesson_btn = gr.Button("Complete Lesson", visible=False)
|
| 283 |
+
|
| 284 |
+
quiz_output = gr.Markdown(visible=False)
|
| 285 |
+
quiz_inputs = []
|
| 286 |
+
for i in range(5):
|
| 287 |
+
quiz_inputs.append(gr.Textbox(label=f"Answer {i+1}", visible=False))
|
| 288 |
+
|
| 289 |
+
quiz_submit_btn = gr.Button("Submit Quiz", visible=False)
|
| 290 |
+
results_output = gr.Markdown(visible=False)
|
| 291 |
+
restart_btn = gr.Button("Start New Lesson", visible=False)
|
| 292 |
+
|
| 293 |
+
with gr.Tab("π Progress Analytics"):
|
| 294 |
+
gr.Markdown("### π Your Learning Analytics Dashboard")
|
| 295 |
+
progress_display = gr.Markdown("Complete some lessons to see your learning analytics!")
|
| 296 |
+
refresh_progress_btn = gr.Button("π Refresh Progress")
|
| 297 |
+
|
| 298 |
+
with gr.Tab("π MCP Server Demo"):
|
| 299 |
+
gr.Markdown("""
|
| 300 |
+
### π€ Model Context Protocol Integration
|
| 301 |
+
|
| 302 |
+
**This app is BOTH a Gradio interface AND an MCP server!**
|
| 303 |
+
|
| 304 |
+
#### π Available MCP Endpoints:
|
| 305 |
+
|
| 306 |
+
- **GET `/mcp/skills`** - List available learning skills
|
| 307 |
+
- **POST `/mcp/lesson/generate`** - Generate personalized lessons
|
| 308 |
+
- **GET `/mcp/progress/{user_id}`** - Get learning progress data
|
| 309 |
+
- **POST `/mcp/quiz/submit`** - Submit quiz answers
|
| 310 |
+
|
| 311 |
+
#### π§ͺ Try the MCP Server:
|
| 312 |
+
|
| 313 |
+
The MCP server is running alongside this Gradio interface! External agents can connect to these endpoints to:
|
| 314 |
+
- Generate lessons for any skill
|
| 315 |
+
- Track learning progress
|
| 316 |
+
- Submit quiz results
|
| 317 |
+
- Access learning analytics
|
| 318 |
+
|
| 319 |
+
**Example MCP Usage:**
|
| 320 |
+
```bash
|
| 321 |
+
# Get available skills
|
| 322 |
+
curl https://your-space-url.com/mcp/skills
|
| 323 |
+
|
| 324 |
+
# Generate a lesson
|
| 325 |
+
curl -X POST https://your-space-url.com/mcp/lesson/generate \\
|
| 326 |
+
-H "Content-Type: application/json" \\
|
| 327 |
+
-d '{"skill": "Python Programming", "user_id": "agent_user"}'
|
| 328 |
+
```
|
| 329 |
+
|
| 330 |
+
#### π― Hackathon Innovation:
|
| 331 |
+
- **Agentic Architecture**: Multiple AI agents (Lesson, Quiz, Progress) collaborate
|
| 332 |
+
- **MCP Protocol**: Full Model Context Protocol implementation
|
| 333 |
+
- **Adaptive Learning**: AI adjusts difficulty based on performance
|
| 334 |
+
- **Real-time Integration**: Seamless connection between UI and MCP endpoints
|
| 335 |
+
""")
|
| 336 |
+
|
| 337 |
+
# MCP Demo Interface
|
| 338 |
+
gr.Markdown("#### π§ͺ Test MCP Endpoints Directly:")
|
| 339 |
+
|
| 340 |
+
with gr.Row():
|
| 341 |
+
with gr.Column():
|
| 342 |
+
mcp_skill_input = gr.Textbox(label="Skill for MCP Test", value="Python Programming")
|
| 343 |
+
mcp_user_input = gr.Textbox(label="User ID for MCP Test", value="mcp_test_user")
|
| 344 |
+
mcp_test_btn = gr.Button("π§ͺ Test MCP Lesson Generation", variant="secondary")
|
| 345 |
+
|
| 346 |
+
with gr.Column():
|
| 347 |
+
mcp_output = gr.JSON(label="MCP Server Response")
|
| 348 |
+
|
| 349 |
+
# Event handlers (same as original app.py)
|
| 350 |
+
async def handle_start_learning(skill_choice, custom_skill_input):
|
| 351 |
+
skill = custom_skill_input.strip() if custom_skill_input.strip() else skill_choice
|
| 352 |
+
if not skill:
|
| 353 |
+
return [
|
| 354 |
+
gr.update(value="β οΈ Please select or enter a skill to continue."),
|
| 355 |
+
gr.update(visible=False),
|
| 356 |
+
gr.update(visible=False),
|
| 357 |
+
skill
|
| 358 |
+
] + [gr.update(visible=False, value="") for _ in range(5)]
|
| 359 |
+
|
| 360 |
+
lesson_content, btn_text, _ = await app_instance.start_lesson(skill)
|
| 361 |
+
|
| 362 |
+
return [
|
| 363 |
+
gr.update(value=lesson_content),
|
| 364 |
+
gr.update(value=btn_text, visible=True),
|
| 365 |
+
gr.update(visible=False),
|
| 366 |
+
skill
|
| 367 |
+
] + [gr.update(visible=False, value="") for _ in range(5)]
|
| 368 |
+
|
| 369 |
+
async def handle_complete_lesson():
|
| 370 |
+
quiz_content, btn_text, _ = await app_instance.complete_lesson_and_start_quiz()
|
| 371 |
+
|
| 372 |
+
quiz_updates = []
|
| 373 |
+
if app_instance.current_quiz:
|
| 374 |
+
for i, question in enumerate(app_instance.current_quiz.questions):
|
| 375 |
+
if i < len(quiz_inputs):
|
| 376 |
+
label = f"Q{i+1}: {question['question'][:50]}..."
|
| 377 |
+
quiz_updates.append(gr.update(label=label, visible=True))
|
| 378 |
+
else:
|
| 379 |
+
quiz_updates.append(gr.update(visible=False))
|
| 380 |
+
for i in range(len(app_instance.current_quiz.questions), len(quiz_inputs)):
|
| 381 |
+
quiz_updates.append(gr.update(visible=False))
|
| 382 |
+
else:
|
| 383 |
+
quiz_updates = [gr.update(visible=False) for _ in range(len(quiz_inputs))]
|
| 384 |
+
|
| 385 |
+
return [
|
| 386 |
+
gr.update(visible=False),
|
| 387 |
+
gr.update(value=quiz_content, visible=True),
|
| 388 |
+
gr.update(value=btn_text, visible=True),
|
| 389 |
+
gr.update(visible=False)
|
| 390 |
+
] + quiz_updates
|
| 391 |
+
|
| 392 |
+
def handle_submit_quiz(*answers):
|
| 393 |
+
valid_answers = [ans for ans in answers if ans is not None and ans != ""]
|
| 394 |
+
results_content, btn_text, _ = app_instance.submit_quiz(*valid_answers)
|
| 395 |
+
|
| 396 |
+
return [
|
| 397 |
+
gr.update(visible=False),
|
| 398 |
+
gr.update(value=results_content, visible=True),
|
| 399 |
+
gr.update(value=btn_text, visible=True),
|
| 400 |
+
gr.update(visible=False)
|
| 401 |
+
] + [gr.update(visible=False) for _ in range(len(quiz_inputs))]
|
| 402 |
+
|
| 403 |
+
def handle_restart():
|
| 404 |
+
return [
|
| 405 |
+
gr.update(visible=False),
|
| 406 |
+
gr.update(visible=False),
|
| 407 |
+
gr.update(visible=False),
|
| 408 |
+
gr.update(visible=False),
|
| 409 |
+
gr.update(visible=False),
|
| 410 |
+
""
|
| 411 |
+
] + [gr.update(visible=False, value="") for _ in range(len(quiz_inputs))]
|
| 412 |
+
|
| 413 |
+
def update_progress_display():
|
| 414 |
+
if not app_instance.progress_agent.user_data:
|
| 415 |
+
return "**No learning data yet.** Complete some lessons to see your progress!"
|
| 416 |
+
|
| 417 |
+
progress_content = "# π Your Learning Progress\n\n"
|
| 418 |
+
for key, progress in app_instance.progress_agent.user_data.items():
|
| 419 |
+
progress_content += f"""
|
| 420 |
+
**Skill:** {progress.skill}
|
| 421 |
+
- Lessons completed: {progress.lessons_completed}
|
| 422 |
+
- Average quiz score: {progress.get_average_score():.1%}
|
| 423 |
+
- Current difficulty: {progress.current_difficulty.title()}
|
| 424 |
+
- Last activity: {progress.last_activity or 'Never'}
|
| 425 |
+
|
| 426 |
+
"""
|
| 427 |
+
return progress_content
|
| 428 |
+
|
| 429 |
+
async def test_mcp_endpoint(skill, user_id):
|
| 430 |
+
"""Test MCP endpoint directly from the interface"""
|
| 431 |
+
try:
|
| 432 |
+
# Simulate MCP endpoint call
|
| 433 |
+
request_data = {
|
| 434 |
+
"skill": skill,
|
| 435 |
+
"user_id": user_id,
|
| 436 |
+
"difficulty": "beginner"
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
# Generate lesson using the app instance
|
| 440 |
+
app_instance.current_user = user_id
|
| 441 |
+
progress = app_instance.progress_agent.get_user_progress(user_id, skill)
|
| 442 |
+
lesson = await app_instance.lesson_agent.generate_lesson(skill, progress.current_difficulty, [])
|
| 443 |
+
|
| 444 |
+
response = {
|
| 445 |
+
"mcp_endpoint": "/mcp/lesson/generate",
|
| 446 |
+
"request": request_data,
|
| 447 |
+
"response": {
|
| 448 |
+
"lesson": {
|
| 449 |
+
"title": lesson.title,
|
| 450 |
+
"content": lesson.content[:200] + "...", # Truncated for display
|
| 451 |
+
"skill": lesson.skill,
|
| 452 |
+
"difficulty": lesson.difficulty,
|
| 453 |
+
"duration_minutes": lesson.duration_minutes,
|
| 454 |
+
"key_concepts": lesson.key_concepts
|
| 455 |
+
},
|
| 456 |
+
"user_context": {
|
| 457 |
+
"user_id": user_id,
|
| 458 |
+
"current_difficulty": progress.current_difficulty,
|
| 459 |
+
"lessons_completed": progress.lessons_completed
|
| 460 |
+
},
|
| 461 |
+
"mcp_server": "Agentic Skill Builder",
|
| 462 |
+
"status": "success"
|
| 463 |
+
}
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
return response
|
| 467 |
+
|
| 468 |
+
except Exception as e:
|
| 469 |
+
return {
|
| 470 |
+
"mcp_endpoint": "/mcp/lesson/generate",
|
| 471 |
+
"error": str(e),
|
| 472 |
+
"status": "error"
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
# Wire up events
|
| 476 |
+
start_btn.click(
|
| 477 |
+
handle_start_learning,
|
| 478 |
+
inputs=[skill_dropdown, custom_skill],
|
| 479 |
+
outputs=[lesson_output, lesson_btn, quiz_output, current_skill] + quiz_inputs[:5]
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
lesson_btn.click(
|
| 483 |
+
handle_complete_lesson,
|
| 484 |
+
outputs=[lesson_btn, quiz_output, quiz_submit_btn, results_output] + quiz_inputs
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
quiz_submit_btn.click(
|
| 488 |
+
handle_submit_quiz,
|
| 489 |
+
inputs=quiz_inputs,
|
| 490 |
+
outputs=[quiz_submit_btn, results_output, restart_btn, quiz_output] + quiz_inputs
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
restart_btn.click(
|
| 494 |
+
handle_restart,
|
| 495 |
+
outputs=[lesson_output, quiz_output, results_output, lesson_btn, restart_btn, current_skill] + quiz_inputs
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
refresh_progress_btn.click(
|
| 499 |
+
update_progress_display,
|
| 500 |
+
outputs=[progress_display]
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
mcp_test_btn.click(
|
| 504 |
+
test_mcp_endpoint,
|
| 505 |
+
inputs=[mcp_skill_input, mcp_user_input],
|
| 506 |
+
outputs=[mcp_output]
|
| 507 |
+
)
|
| 508 |
+
|
| 509 |
+
return demo
|
| 510 |
+
|
| 511 |
+
# ===== MAIN APPLICATION =====
|
| 512 |
+
|
| 513 |
+
def run_mcp_server():
|
| 514 |
+
"""Run the MCP server in a separate thread"""
|
| 515 |
+
uvicorn.run(
|
| 516 |
+
mcp_app,
|
| 517 |
+
host="0.0.0.0",
|
| 518 |
+
port=8001, # Different port to avoid conflicts
|
| 519 |
+
log_level="info"
|
| 520 |
+
)
|
| 521 |
+
|
| 522 |
+
def main():
|
| 523 |
+
"""Main application entry point for Hugging Face Spaces"""
|
| 524 |
+
# Start MCP server in background thread
|
| 525 |
+
mcp_thread = threading.Thread(target=run_mcp_server, daemon=True)
|
| 526 |
+
mcp_thread.start()
|
| 527 |
+
|
| 528 |
+
# Give MCP server time to start
|
| 529 |
+
time.sleep(2)
|
| 530 |
+
|
| 531 |
+
# Create and launch Gradio interface
|
| 532 |
+
demo = create_interface()
|
| 533 |
+
demo.launch(
|
| 534 |
+
server_name="0.0.0.0",
|
| 535 |
+
server_port=7860,
|
| 536 |
+
share=True, # Enable sharing for demo purposes
|
| 537 |
+
show_error=True
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
if __name__ == "__main__":
|
| 541 |
+
main()
|
validate_hackathon.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Hackathon Validation Script
|
| 3 |
+
Tests the MCP server functionality for submission requirements
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import asyncio
|
| 7 |
+
import requests
|
| 8 |
+
import time
|
| 9 |
+
import subprocess
|
| 10 |
+
import threading
|
| 11 |
+
from datetime import datetime
|
| 12 |
+
|
| 13 |
+
def test_mcp_server_endpoints():
|
| 14 |
+
"""Test MCP server endpoints to ensure hackathon compliance"""
|
| 15 |
+
print("π§ͺ HACKATHON VALIDATION - MCP SERVER TESTING")
|
| 16 |
+
print("=" * 60)
|
| 17 |
+
|
| 18 |
+
base_url = "http://localhost:8001" # MCP server port
|
| 19 |
+
|
| 20 |
+
tests = [
|
| 21 |
+
("Root endpoint", "GET", "/"),
|
| 22 |
+
("Skills list", "GET", "/mcp/skills"),
|
| 23 |
+
("Progress endpoint", "GET", "/mcp/progress/test_user"),
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
print(f"π Testing MCP server at {base_url}")
|
| 27 |
+
print(f"π Running {len(tests)} endpoint tests...\n")
|
| 28 |
+
|
| 29 |
+
results = []
|
| 30 |
+
|
| 31 |
+
for test_name, method, endpoint in tests:
|
| 32 |
+
try:
|
| 33 |
+
url = f"{base_url}{endpoint}"
|
| 34 |
+
print(f"π Testing {test_name}: {method} {endpoint}")
|
| 35 |
+
|
| 36 |
+
if method == "GET":
|
| 37 |
+
response = requests.get(url, timeout=5)
|
| 38 |
+
elif method == "POST":
|
| 39 |
+
response = requests.post(url, json={}, timeout=5)
|
| 40 |
+
|
| 41 |
+
if response.status_code == 200:
|
| 42 |
+
print(f" β
SUCCESS: {response.status_code}")
|
| 43 |
+
try:
|
| 44 |
+
data = response.json()
|
| 45 |
+
if "mcp" in str(data).lower() or "agentic" in str(data).lower():
|
| 46 |
+
print(f" π― MCP-compliant response detected")
|
| 47 |
+
except:
|
| 48 |
+
pass
|
| 49 |
+
results.append((test_name, True, response.status_code))
|
| 50 |
+
else:
|
| 51 |
+
print(f" β FAILED: {response.status_code}")
|
| 52 |
+
results.append((test_name, False, response.status_code))
|
| 53 |
+
|
| 54 |
+
except requests.exceptions.RequestException as e:
|
| 55 |
+
print(f" β CONNECTION ERROR: {e}")
|
| 56 |
+
results.append((test_name, False, "Connection Error"))
|
| 57 |
+
|
| 58 |
+
print()
|
| 59 |
+
|
| 60 |
+
# Summary
|
| 61 |
+
print("π TEST SUMMARY")
|
| 62 |
+
print("-" * 40)
|
| 63 |
+
passed = sum(1 for _, success, _ in results if success)
|
| 64 |
+
total = len(results)
|
| 65 |
+
print(f"β
Passed: {passed}/{total}")
|
| 66 |
+
|
| 67 |
+
if passed == total:
|
| 68 |
+
print("π ALL MCP ENDPOINT TESTS PASSED!")
|
| 69 |
+
else:
|
| 70 |
+
print("β οΈ Some tests failed. Check the MCP server.")
|
| 71 |
+
|
| 72 |
+
return results
|
| 73 |
+
|
| 74 |
+
def test_post_endpoints():
|
| 75 |
+
"""Test POST endpoints with sample data"""
|
| 76 |
+
print("\nπ§ͺ TESTING POST ENDPOINTS")
|
| 77 |
+
print("=" * 40)
|
| 78 |
+
|
| 79 |
+
base_url = "http://localhost:8001"
|
| 80 |
+
|
| 81 |
+
# Test lesson generation
|
| 82 |
+
try:
|
| 83 |
+
print("π Testing lesson generation...")
|
| 84 |
+
lesson_data = {
|
| 85 |
+
"skill": "Python Programming",
|
| 86 |
+
"user_id": "test_user",
|
| 87 |
+
"difficulty": "beginner"
|
| 88 |
+
}
|
| 89 |
+
response = requests.post(f"{base_url}/mcp/lesson/generate", json=lesson_data, timeout=10)
|
| 90 |
+
if response.status_code == 200:
|
| 91 |
+
print(" β
Lesson generation successful")
|
| 92 |
+
data = response.json()
|
| 93 |
+
if "lesson" in data:
|
| 94 |
+
print(" π― Lesson data structure valid")
|
| 95 |
+
else:
|
| 96 |
+
print(f" β Lesson generation failed: {response.status_code}")
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f" β Lesson generation error: {e}")
|
| 99 |
+
|
| 100 |
+
# Test quiz submission
|
| 101 |
+
try:
|
| 102 |
+
print("π Testing quiz submission...")
|
| 103 |
+
quiz_data = {
|
| 104 |
+
"user_id": "test_user",
|
| 105 |
+
"skill": "Python Programming",
|
| 106 |
+
"lesson_title": "Variables and Data Types",
|
| 107 |
+
"answers": ["correct", "incorrect", "correct"]
|
| 108 |
+
}
|
| 109 |
+
response = requests.post(f"{base_url}/mcp/quiz/submit", json=quiz_data, timeout=10)
|
| 110 |
+
if response.status_code == 200:
|
| 111 |
+
print(" β
Quiz submission successful")
|
| 112 |
+
else:
|
| 113 |
+
print(f" β Quiz submission failed: {response.status_code}")
|
| 114 |
+
except Exception as e:
|
| 115 |
+
print(f" β Quiz submission error: {e}")
|
| 116 |
+
|
| 117 |
+
def validate_hackathon_requirements():
|
| 118 |
+
"""Validate all hackathon submission requirements"""
|
| 119 |
+
print("\nπ HACKATHON SUBMISSION VALIDATION")
|
| 120 |
+
print("=" * 50)
|
| 121 |
+
|
| 122 |
+
requirements = []
|
| 123 |
+
|
| 124 |
+
# Check README.md for required tag
|
| 125 |
+
try:
|
| 126 |
+
with open("README.md", "r", encoding="utf-8") as f:
|
| 127 |
+
readme_content = f.read()
|
| 128 |
+
if "mcp-server-track" in readme_content:
|
| 129 |
+
print("β
README.md contains 'mcp-server-track' tag")
|
| 130 |
+
requirements.append(("README tag", True))
|
| 131 |
+
else:
|
| 132 |
+
print("β README.md missing 'mcp-server-track' tag")
|
| 133 |
+
requirements.append(("README tag", False))
|
| 134 |
+
except FileNotFoundError:
|
| 135 |
+
print("β README.md not found")
|
| 136 |
+
requirements.append(("README file", False))
|
| 137 |
+
|
| 138 |
+
# Check for demo video link
|
| 139 |
+
try:
|
| 140 |
+
with open("README.md", "r", encoding="utf-8") as f:
|
| 141 |
+
readme_content = f.read()
|
| 142 |
+
if "demo-video-link.com" in readme_content or "your-demo-video-link.com" in readme_content:
|
| 143 |
+
print("β οΈ Demo video link is placeholder - needs actual video")
|
| 144 |
+
requirements.append(("Demo video", False))
|
| 145 |
+
elif any(video_keyword in readme_content.lower() for video_keyword in ["youtube.com", "vimeo.com", "loom.com", "demo video"]):
|
| 146 |
+
print("β
Demo video link appears to be present")
|
| 147 |
+
requirements.append(("Demo video", True))
|
| 148 |
+
else:
|
| 149 |
+
print("β Demo video link not found")
|
| 150 |
+
requirements.append(("Demo video", False))
|
| 151 |
+
except:
|
| 152 |
+
requirements.append(("Demo video check", False))
|
| 153 |
+
|
| 154 |
+
# Check space_app.py exists and has MCP endpoints
|
| 155 |
+
try:
|
| 156 |
+
with open("space_app.py", "r", encoding="utf-8") as f:
|
| 157 |
+
app_content = f.read()
|
| 158 |
+
if "FastAPI" in app_content and "@mcp_app" in app_content:
|
| 159 |
+
print("β
space_app.py has MCP server integration")
|
| 160 |
+
requirements.append(("MCP integration", True))
|
| 161 |
+
else:
|
| 162 |
+
print("β space_app.py missing MCP server integration")
|
| 163 |
+
requirements.append(("MCP integration", False))
|
| 164 |
+
except FileNotFoundError:
|
| 165 |
+
print("β space_app.py not found")
|
| 166 |
+
requirements.append(("Main app file", False))
|
| 167 |
+
|
| 168 |
+
# Check requirements.txt
|
| 169 |
+
try:
|
| 170 |
+
with open("requirements.txt", "r") as f:
|
| 171 |
+
reqs = f.read()
|
| 172 |
+
if "gradio" in reqs and "fastapi" in reqs:
|
| 173 |
+
print("β
requirements.txt has necessary dependencies")
|
| 174 |
+
requirements.append(("Dependencies", True))
|
| 175 |
+
else:
|
| 176 |
+
print("β requirements.txt missing key dependencies")
|
| 177 |
+
requirements.append(("Dependencies", False))
|
| 178 |
+
except FileNotFoundError:
|
| 179 |
+
print("β requirements.txt not found")
|
| 180 |
+
requirements.append(("Requirements file", False))
|
| 181 |
+
|
| 182 |
+
print("\nπ SUBMISSION CHECKLIST")
|
| 183 |
+
print("-" * 30)
|
| 184 |
+
passed = sum(1 for _, success in requirements if success)
|
| 185 |
+
total = len(requirements)
|
| 186 |
+
|
| 187 |
+
for req_name, success in requirements:
|
| 188 |
+
status = "β
" if success else "β"
|
| 189 |
+
print(f"{status} {req_name}")
|
| 190 |
+
|
| 191 |
+
print(f"\nπ Overall Score: {passed}/{total}")
|
| 192 |
+
|
| 193 |
+
if passed == total:
|
| 194 |
+
print("π READY FOR HACKATHON SUBMISSION!")
|
| 195 |
+
else:
|
| 196 |
+
print("β οΈ Please address the failed requirements above.")
|
| 197 |
+
|
| 198 |
+
return requirements
|
| 199 |
+
|
| 200 |
+
def main():
|
| 201 |
+
"""Main validation function"""
|
| 202 |
+
print("π AGENTIC SKILL BUILDER - HACKATHON VALIDATION")
|
| 203 |
+
print("=" * 60)
|
| 204 |
+
print(f"β° Validation Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
| 205 |
+
print()
|
| 206 |
+
|
| 207 |
+
# First validate file-based requirements
|
| 208 |
+
validate_hackathon_requirements()
|
| 209 |
+
|
| 210 |
+
# Ask user if they want to test the running server
|
| 211 |
+
print("\n" + "="*60)
|
| 212 |
+
print("π MCP SERVER TESTING")
|
| 213 |
+
print("To test MCP endpoints, the server should be running on localhost:8001")
|
| 214 |
+
print("You can start it with: python space_app.py")
|
| 215 |
+
|
| 216 |
+
try:
|
| 217 |
+
# Try to test if server is running
|
| 218 |
+
response = requests.get("http://localhost:8001", timeout=2)
|
| 219 |
+
print("β
Server detected running!")
|
| 220 |
+
test_mcp_server_endpoints()
|
| 221 |
+
test_post_endpoints()
|
| 222 |
+
except:
|
| 223 |
+
print("β οΈ Server not detected. Start the server to test MCP endpoints.")
|
| 224 |
+
print(" Command: python space_app.py")
|
| 225 |
+
|
| 226 |
+
print("\n" + "="*60)
|
| 227 |
+
print("π― NEXT STEPS FOR HACKATHON SUBMISSION:")
|
| 228 |
+
print("1. πΉ Record demo video showing MCP server in action")
|
| 229 |
+
print("2. π Update README.md with actual demo video link")
|
| 230 |
+
print("3. π Upload to Hugging Face Spaces under Agents-MCP-Hackathon org")
|
| 231 |
+
print("4. β
Ensure all MCP endpoints work in the deployed Space")
|
| 232 |
+
print("=" * 60)
|
| 233 |
+
|
| 234 |
+
if __name__ == "__main__":
|
| 235 |
+
main()
|