Ali Abdullah commited on
Commit
a0cbb48
·
verified ·
1 Parent(s): ce14c32

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +809 -97
app.py CHANGED
@@ -1,133 +1,845 @@
1
  import gradio as gr
2
- import requests
 
3
  import json
 
 
 
 
 
4
  import time
5
 
6
- # Your backend URL (you can change this to your Flask backend)
7
- BACKEND_URL = "http://localhost:5000/api/chat" # Change this when you deploy your backend
8
 
9
- def chat_with_bot(message, history):
10
- """
11
- Function to handle chat with the bot
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  """
 
 
 
13
  if not message.strip():
14
  return history, ""
15
 
16
  # Add user message to history
 
 
 
17
  history = history + [[message, None]]
18
 
19
  try:
20
  # Simulate typing delay
21
  time.sleep(1)
22
 
23
- # For demo purposes, we'll create a simple response
24
- # Replace this with actual API call to your backend
25
- response = f"Thanks for asking about '{message}'! This is atomcamp AI assistant. I can help you with:\n\n• Data Science courses\n• Machine Learning concepts\n• Programming tutorials\n• Career guidance\n\nWhat specific topic would you like to explore?"
 
 
 
26
 
27
- # You can uncomment this when you have your backend ready:
28
- # response_data = requests.post(BACKEND_URL, json={"message": message})
29
- # response = response_data.json().get("response", "Sorry, I couldn't process your request.")
30
 
31
  # Add bot response to history
32
  history[-1][1] = response
33
 
34
  except Exception as e:
35
- history[-1][1] = "Sorry, I'm having trouble connecting to the server. Please try again later."
36
 
37
  return history, ""
38
 
39
- # Custom CSS for atomcamp branding
40
- custom_css = """
41
- .gradio-container {
42
- font-family: 'Inter', sans-serif !important;
43
- }
44
-
45
- .chat-message {
46
- font-size: 14px !important;
47
- }
48
-
49
- #chatbot {
50
- height: 500px !important;
51
- }
52
-
53
- .message.user {
54
- background: linear-gradient(135deg, #22c55e, #16a34a) !important;
55
- color: white !important;
56
- }
57
-
58
- .message.bot {
59
- background: #f3f4f6 !important;
60
- color: #374151 !important;
61
- }
62
-
63
- .contain {
64
- max-width: 800px !important;
65
- margin: 0 auto !important;
66
- }
67
-
68
- footer {
69
- display: none !important;
70
- }
71
- """
72
-
73
- # Create the Gradio interface
74
  with gr.Blocks(
75
- css=custom_css,
76
  title="atomcamp AI Chatbot",
77
- theme=gr.themes.Soft(
78
- primary_hue="green",
79
- secondary_hue="gray",
80
- neutral_hue="gray",
81
- )
 
 
 
 
 
82
  ) as demo:
83
 
84
- # Header
85
- gr.HTML("""
86
- <div style="text-align: center; padding: 2rem 0; background: linear-gradient(135deg, #f8fafc, #f1f5f9); border-radius: 10px; margin-bottom: 2rem;">
87
- <div style="background: #22c55e; color: white; padding: 12px 24px; border-radius: 24px; display: inline-block; font-weight: 700; font-size: 18px; margin-bottom: 1rem;">
88
- atomcamp
89
- </div>
90
- <h1 style="color: #166534; font-size: 2rem; font-weight: 600; margin: 0;">AI Chatbot Assistant</h1>
91
- <p style="color: #6b7280; margin-top: 0.5rem;">Ask me about data science, courses, and learning paths!</p>
92
- </div>
93
- """)
94
-
95
- # Chat interface
96
- chatbot = gr.Chatbot(
97
- elem_id="chatbot",
98
- label="Chat with atomcamp AI",
99
- show_label=False,
100
- avatar_images=["👤", "🤖"],
101
- bubble_full_width=False,
102
- show_share_button=False,
103
- )
104
 
105
- # Input area
106
- with gr.Row():
107
- msg = gr.Textbox(
108
- placeholder="Ask me anything about atomcamp...",
109
- show_label=False,
110
- scale=4,
111
- container=False
112
- )
113
- submit_btn = gr.Button("Send", variant="primary", scale=1)
114
 
115
- # Footer info
116
- gr.HTML("""
117
- <div style="text-align: center; padding: 1rem; color: #6b7280; font-size: 0.875rem; margin-top: 2rem;">
118
- <p>🚀 Built with ❤️ for atomcamp | Data Science Education Platform</p>
119
- <p>💡 This chatbot can help you with courses, concepts, and career guidance</p>
120
- </div>
121
- """)
122
-
123
- # Event handlers
124
- msg.submit(chat_with_bot, [msg, chatbot], [chatbot, msg])
125
- submit_btn.click(chat_with_bot, [msg, chatbot], [chatbot, msg])
 
 
 
 
 
 
126
 
127
- # Launch the app
128
  if __name__ == "__main__":
129
  demo.launch(
130
  server_name="0.0.0.0",
131
  server_port=7860,
132
- share=True
133
- )
 
 
1
  import gradio as gr
2
+ import os
3
+ from dotenv import load_dotenv
4
  import json
5
+ from langchain_community.vectorstores import FAISS
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain_core.prompts import ChatPromptTemplate
8
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
9
+ from langchain_core.documents import Document
10
  import time
11
 
12
+ # Load environment variables
13
+ load_dotenv()
14
 
15
+ # Initialize embeddings
16
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
17
+
18
+ # Global variables
19
+ vectorstore = None
20
+ retriever = None
21
+
22
+ def initialize_vectorstore():
23
+ """Initialize or create vectorstore"""
24
+ global vectorstore, retriever
25
+
26
+ try:
27
+ # Try to load existing vectorstore
28
+ vectorstore = FAISS.load_local("atomcamp_vector_db", embeddings, allow_dangerous_deserialization=True)
29
+ retriever = vectorstore.as_retriever()
30
+ return "✅ Vectorstore loaded successfully!"
31
+ except:
32
+ # If no vectorstore exists, create sample data
33
+ sample_data = [
34
+ {
35
+ "text": "Atomcamp is a leading data science education platform offering comprehensive courses in machine learning, Python programming, data analysis, and AI. We provide hands-on projects, expert mentorship, and career guidance to help students become successful data scientists.",
36
+ "url": "https://www.atomcamp.com/about"
37
+ },
38
+ {
39
+ "text": "Our courses include: Python for Data Science, Machine Learning Fundamentals, Deep Learning with TensorFlow, Data Visualization with Matplotlib and Seaborn, SQL for Data Analysis, Statistics for Data Science, and Advanced AI Techniques.",
40
+ "url": "https://www.atomcamp.com/courses"
41
+ },
42
+ {
43
+ "text": "Atomcamp offers flexible learning paths: Beginner Track (3 months) - Python basics, data manipulation, basic statistics. Intermediate Track (6 months) - Machine learning, advanced Python, real projects. Advanced Track (9 months) - Deep learning, AI, industry projects, job placement assistance.",
44
+ "url": "https://www.atomcamp.com/learning-paths"
45
+ },
46
+ {
47
+ "text": "Career services at Atomcamp include: Resume building, Interview preparation, Portfolio development, Job placement assistance, Industry networking events, Mentorship programs, and Career counseling sessions.",
48
+ "url": "https://www.atomcamp.com/career-services"
49
+ },
50
+ {
51
+ "text": "Atomcamp instructors are industry experts with 10+ years of experience at top companies like Google, Microsoft, Amazon, and Netflix. They bring real-world experience and practical knowledge to help students succeed in their data science careers.",
52
+ "url": "https://www.atomcamp.com/instructors"
53
+ }
54
+ ]
55
+
56
+ # Create documents
57
+ docs = []
58
+ for item in sample_data:
59
+ docs.append(Document(page_content=item["text"], metadata={"url": item["url"]}))
60
+
61
+ # Split documents
62
+ splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
63
+ chunks = splitter.split_documents(docs)
64
+
65
+ # Create vectorstore
66
+ vectorstore = FAISS.from_documents(chunks, embeddings)
67
+ vectorstore.save_local("atomcamp_vector_db")
68
+ retriever = vectorstore.as_retriever()
69
+
70
+ return "✅ Sample vectorstore created successfully!"
71
+
72
+ def generate_response(message, context):
73
+ """Generate contextual responses"""
74
+ message_lower = message.lower()
75
+
76
+ # Course-related queries
77
+ if any(word in message_lower for word in ['course', 'courses', 'learn', 'study', 'curriculum']):
78
+ return """🎓 **Atomcamp Courses:**
79
+
80
+ **Core Programs:**
81
+ • **Python for Data Science** - Master Python programming fundamentals
82
+ • **Machine Learning Fundamentals** - Learn ML algorithms and applications
83
+ • **Deep Learning with TensorFlow** - Build neural networks and AI models
84
+ • **Data Visualization** - Create stunning charts with Matplotlib & Seaborn
85
+ • **SQL for Data Analysis** - Database querying and data manipulation
86
+ • **Statistics for Data Science** - Statistical analysis and hypothesis testing
87
+
88
+ **Learning Tracks:**
89
+ • **Beginner Track** (3 months) - Perfect for newcomers
90
+ • **Intermediate Track** (6 months) - Build real-world projects
91
+ • **Advanced Track** (9 months) - Industry-ready with job placement
92
+
93
+ Would you like details about any specific course?"""
94
+
95
+ # Career-related queries
96
+ elif any(word in message_lower for word in ['career', 'job', 'placement', 'salary', 'work']):
97
+ return """💼 **Career Services at Atomcamp:**
98
+
99
+ **Job Placement Support:**
100
+ • Resume building and optimization
101
+ • Technical interview preparation
102
+ • Portfolio development guidance
103
+ • Direct connections with hiring partners
104
+ • Mock interviews with industry experts
105
+
106
+ **Career Growth:**
107
+ • Average salary increase: 150-300%
108
+ • 95% job placement rate within 6 months
109
+ • Access to exclusive job opportunities
110
+ • Ongoing career mentorship
111
+ • Industry networking events
112
+
113
+ **Success Stories:**
114
+ Our graduates work at top companies like Google, Microsoft, Amazon, Netflix, and leading startups worldwide.
115
+
116
+ Ready to transform your career in data science?"""
117
+
118
+ # About Atomcamp
119
+ elif any(word in message_lower for word in ['about', 'atomcamp', 'company', 'who', 'what']):
120
+ return """🚀 **About Atomcamp:**
121
+
122
+ Atomcamp is a premier data science education platform dedicated to transforming careers through practical, industry-focused learning.
123
+
124
+ **What Makes Us Special:**
125
+ • **Expert Instructors** - Industry professionals with 10+ years experience
126
+ • **Hands-on Learning** - Real projects, not just theory
127
+ • **Personalized Mentorship** - 1-on-1 guidance throughout your journey
128
+ • **Job Guarantee** - 95% placement rate with salary increases up to 300%
129
+ • **Flexible Learning** - Online, part-time, and full-time options
130
+
131
+ **Our Mission:**
132
+ To make data science education accessible, practical, and career-focused, helping students become job-ready data scientists."""
133
+
134
+ # Machine Learning specific
135
+ elif any(word in message_lower for word in ['machine learning', 'ml', 'algorithm', 'model']):
136
+ return """🤖 **Machine Learning at Atomcamp:**
137
+
138
+ **Core ML Topics Covered:**
139
+ • **Supervised Learning** - Linear/Logistic Regression, Decision Trees, Random Forest
140
+ • **Unsupervised Learning** - Clustering, PCA, Association Rules
141
+ • **Deep Learning** - Neural Networks, CNN, RNN, LSTM
142
+ • **Natural Language Processing** - Text analysis, sentiment analysis
143
+ • **Computer Vision** - Image recognition, object detection
144
+
145
+ **Practical Projects:**
146
+ • Predictive analytics for business
147
+ • Recommendation systems
148
+ • Fraud detection models
149
+ • Customer segmentation
150
+ • Price prediction algorithms
151
+
152
+ Want to dive deeper into any specific ML topic?"""
153
+
154
+ # Python programming
155
+ elif any(word in message_lower for word in ['python', 'programming', 'code', 'coding']):
156
+ return """🐍 **Python for Data Science at Atomcamp:**
157
+
158
+ **Python Fundamentals:**
159
+ • Variables, data types, and control structures
160
+ • Functions, classes, and object-oriented programming
161
+ • File handling and error management
162
+ • Libraries and package management
163
+
164
+ **Data Science Libraries:**
165
+ • **Pandas** - Data manipulation and analysis
166
+ • **NumPy** - Numerical computing
167
+ • **Matplotlib/Seaborn** - Data visualization
168
+ • **Scikit-learn** - Machine learning
169
+ • **TensorFlow/PyTorch** - Deep learning
170
+
171
+ Perfect for beginners and experienced programmers alike!"""
172
+
173
+ # Getting started
174
+ elif any(word in message_lower for word in ['start', 'begin', 'beginner', 'new', 'how']):
175
+ return """🌟 **Getting Started with Atomcamp:**
176
+
177
+ **Step 1: Choose Your Path**
178
+ • **Complete Beginner?** → Start with Python Fundamentals
179
+ • **Some Programming Experience?** → Jump to Data Science Essentials
180
+ • **Want to Specialize?** → Pick Machine Learning or AI track
181
+
182
+ **Step 2: Learning Format**
183
+ • **Self-paced Online** - Learn at your own speed
184
+ • **Live Cohorts** - Interactive sessions with peers
185
+ • **1-on-1 Mentorship** - Personalized guidance
186
+
187
+ Ready to begin your data science journey?"""
188
+
189
+ # Default response with context
190
+ else:
191
+ return f"""Thank you for your question about "{message}"!
192
+
193
+ As an Atomcamp AI assistant, I'm here to help you with:
194
+
195
+ 🎓 **Course Information** - Learn about our data science programs
196
+ 💼 **Career Guidance** - Job placement and career growth
197
+ 🐍 **Technical Topics** - Python, ML, AI, and data analysis
198
+ 🚀 **Getting Started** - How to begin your data science journey
199
+
200
+ Would you like me to elaborate on any specific aspect?"""
201
+
202
+ # Initialize vectorstore on startup
203
+ init_message = initialize_vectorstore()
204
+
205
+ # Create the custom HTML interface
206
+ def create_chat_interface():
207
+ return """
208
+ <!DOCTYPE html>
209
+ <html lang="en">
210
+ <head>
211
+ <meta charset="UTF-8">
212
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
213
+ <title>atomcamp AI Chatbot</title>
214
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
215
+ <style>
216
+ * {
217
+ margin: 0;
218
+ padding: 0;
219
+ box-sizing: border-box;
220
+ }
221
+
222
+ body {
223
+ font-family: 'Inter', sans-serif;
224
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
225
+ min-height: 100vh;
226
+ display: flex;
227
+ flex-direction: column;
228
+ }
229
+
230
+ .header {
231
+ text-align: center;
232
+ padding: 2rem 1rem;
233
+ }
234
+
235
+ .logo {
236
+ width: 160px;
237
+ height: 48px;
238
+ margin: 0 auto 1.5rem;
239
+ background: #22c55e;
240
+ border-radius: 24px;
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ color: white;
245
+ font-weight: 700;
246
+ font-size: 18px;
247
+ }
248
+
249
+ .title-bubble {
250
+ background: #f0fdf4;
251
+ border: 1px solid #bbf7d0;
252
+ border-radius: 1rem;
253
+ padding: 1.5rem;
254
+ display: inline-block;
255
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
256
+ max-width: fit-content;
257
+ }
258
+
259
+ .title {
260
+ font-size: 1.5rem;
261
+ font-weight: 600;
262
+ color: #166534;
263
+ margin-bottom: 0.5rem;
264
+ text-align: left;
265
+ }
266
+
267
+ .status {
268
+ display: flex;
269
+ align-items: center;
270
+ gap: 0.5rem;
271
+ font-size: 0.875rem;
272
+ font-weight: 500;
273
+ text-align: left;
274
+ }
275
+
276
+ .status-dot {
277
+ width: 8px;
278
+ height: 8px;
279
+ border-radius: 50%;
280
+ background: #22c55e;
281
+ }
282
+
283
+ .status.typing .status-dot {
284
+ animation: pulse 1s infinite;
285
+ }
286
+
287
+ .typing-dots {
288
+ display: flex;
289
+ gap: 4px;
290
+ margin-right: 8px;
291
+ }
292
+
293
+ .typing-dot {
294
+ width: 6px;
295
+ height: 6px;
296
+ background: #16a34a;
297
+ border-radius: 50%;
298
+ animation: bounce 1.4s infinite ease-in-out;
299
+ }
300
+
301
+ .typing-dot:nth-child(1) { animation-delay: -0.32s; }
302
+ .typing-dot:nth-child(2) { animation-delay: -0.16s; }
303
+
304
+ .chat-container {
305
+ flex: 1;
306
+ max-width: 4rem;
307
+ margin: 0 auto;
308
+ padding: 0 1rem;
309
+ overflow-y: auto;
310
+ max-height: calc(100vh - 300px);
311
+ }
312
+
313
+ .welcome-screen {
314
+ text-align: center;
315
+ padding: 3rem 1rem;
316
+ }
317
+
318
+ .welcome-card {
319
+ background: white;
320
+ border-radius: 1rem;
321
+ padding: 2rem;
322
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
323
+ max-width: 600px;
324
+ margin: 0 auto;
325
+ }
326
+
327
+ .welcome-icon {
328
+ width: 48px;
329
+ height: 48px;
330
+ background: #22c55e;
331
+ border-radius: 12px;
332
+ display: flex;
333
+ align-items: center;
334
+ justify-content: center;
335
+ margin: 0 auto 1rem;
336
+ color: white;
337
+ font-size: 24px;
338
+ }
339
+
340
+ .message {
341
+ margin-bottom: 1rem;
342
+ display: flex;
343
+ align-items: flex-end;
344
+ gap: 0.5rem;
345
+ animation: fadeIn 0.3s ease-out;
346
+ }
347
+
348
+ .message.user {
349
+ justify-content: flex-end;
350
+ }
351
+
352
+ .message-bubble {
353
+ max-width: 70%;
354
+ padding: 0.75rem 1rem;
355
+ border-radius: 1rem;
356
+ font-size: 0.875rem;
357
+ line-height: 1.5;
358
+ }
359
+
360
+ .message.user .message-bubble {
361
+ background: #22c55e;
362
+ color: white;
363
+ border-bottom-right-radius: 0.25rem;
364
+ }
365
+
366
+ .message.bot .message-bubble {
367
+ background: #f3f4f6;
368
+ color: #374151;
369
+ border-bottom-left-radius: 0.25rem;
370
+ }
371
+
372
+ .avatar {
373
+ width: 32px;
374
+ height: 32px;
375
+ border-radius: 50%;
376
+ display: flex;
377
+ align-items: center;
378
+ justify-content: center;
379
+ font-size: 14px;
380
+ flex-shrink: 0;
381
+ }
382
+
383
+ .avatar.user {
384
+ background: #22c55e;
385
+ color: white;
386
+ }
387
+
388
+ .avatar.bot {
389
+ background: #e5e7eb;
390
+ color: #6b7280;
391
+ }
392
+
393
+ .typing-indicator {
394
+ display: flex;
395
+ align-items: center;
396
+ gap: 0.5rem;
397
+ padding: 0.75rem 1rem;
398
+ background: #f3f4f6;
399
+ border-radius: 1rem;
400
+ border-bottom-left-radius: 0.25rem;
401
+ max-width: 70%;
402
+ }
403
+
404
+ .input-area {
405
+ position: fixed;
406
+ bottom: 0;
407
+ left: 0;
408
+ right: 0;
409
+ background: rgba(255, 255, 255, 0.95);
410
+ backdrop-filter: blur(10px);
411
+ border-top: 1px solid #e5e7eb;
412
+ padding: 1rem;
413
+ }
414
+
415
+ .input-container {
416
+ max-width: 4rem;
417
+ margin: 0 auto;
418
+ position: relative;
419
+ }
420
+
421
+ .input-wrapper {
422
+ background: #4b5563;
423
+ border-radius: 2rem;
424
+ display: flex;
425
+ align-items: center;
426
+ padding: 0.5rem;
427
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
428
+ }
429
+
430
+ .message-input {
431
+ flex: 1;
432
+ background: transparent;
433
+ border: none;
434
+ outline: none;
435
+ color: white;
436
+ padding: 0.75rem 1rem;
437
+ font-size: 0.875rem;
438
+ font-weight: 500;
439
+ }
440
+
441
+ .message-input::placeholder {
442
+ color: #d1d5db;
443
+ }
444
+
445
+ .send-button {
446
+ background: #22c55e;
447
+ border: none;
448
+ border-radius: 50%;
449
+ width: 40px;
450
+ height: 40px;
451
+ display: flex;
452
+ align-items: center;
453
+ justify-content: center;
454
+ color: white;
455
+ cursor: pointer;
456
+ transition: all 0.2s;
457
+ }
458
+
459
+ .send-button:hover:not(:disabled) {
460
+ background: #16a34a;
461
+ transform: scale(1.05);
462
+ }
463
+
464
+ .send-button:disabled {
465
+ opacity: 0.5;
466
+ cursor: not-allowed;
467
+ }
468
+
469
+ .status-bar {
470
+ display: flex;
471
+ justify-content: space-between;
472
+ align-items: center;
473
+ margin-top: 0.5rem;
474
+ padding: 0 1rem;
475
+ font-size: 0.75rem;
476
+ color: #6b7280;
477
+ font-weight: 500;
478
+ }
479
+
480
+ @keyframes pulse {
481
+ 0%, 100% { opacity: 1; }
482
+ 50% { opacity: 0.5; }
483
+ }
484
+
485
+ @keyframes bounce {
486
+ 0%, 80%, 100% { transform: scale(0); }
487
+ 40% { transform: scale(1); }
488
+ }
489
+
490
+ @keyframes fadeIn {
491
+ from { opacity: 0; transform: translateY(8px); }
492
+ to { opacity: 1; transform: translateY(0); }
493
+ }
494
+
495
+ @media (max-width: 768px) {
496
+ .chat-container {
497
+ max-width: 100%;
498
+ }
499
+ .input-container {
500
+ max-width: 100%;
501
+ }
502
+ }
503
+ </style>
504
+ </head>
505
+ <body>
506
+ <!-- Header -->
507
+ <div class="header">
508
+ <div class="logo">atomcamp</div>
509
+ <div class="title-bubble">
510
+ <div class="title">Chatbot</div>
511
+ <div class="status" id="status">
512
+ <div class="status-dot"></div>
513
+ <span id="status-text">Online</span>
514
+ </div>
515
+ </div>
516
+ </div>
517
+
518
+ <!-- Chat Container -->
519
+ <div class="chat-container" id="chatContainer">
520
+ <div class="welcome-screen" id="welcomeScreen">
521
+ <div class="welcome-card">
522
+ <div class="welcome-icon">✨</div>
523
+ <h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1rem; color: #374151;">Welcome to atomcamp AI</h2>
524
+ <p style="color: #6b7280; margin-bottom: 2rem; font-size: 0.875rem; line-height: 1.5;">Ask me about courses, data science concepts, and learning paths.</p>
525
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; text-align: left;">
526
+ <div style="background: #f9fafb; padding: 1rem; border-radius: 0.5rem;">
527
+ <h3 style="font-weight: 600; margin-bottom: 0.5rem; font-size: 0.875rem;">Ask me about:</h3>
528
+ <ul style="font-size: 0.75rem; color: #6b7280; line-height: 1.5;">
529
+ <li>• Course information</li>
530
+ <li>• Data science concepts</li>
531
+ <li>• Learning paths</li>
532
+ </ul>
533
+ </div>
534
+ <div style="background: #f9fafb; padding: 1rem; border-radius: 0.5rem;">
535
+ <h3 style="font-weight: 600; margin-bottom: 0.5rem; font-size: 0.875rem;">Try asking:</h3>
536
+ <ul style="font-size: 0.75rem; color: #6b7280; line-height: 1.5;">
537
+ <li>• "What courses do you offer?"</li>
538
+ <li>• "Explain machine learning"</li>
539
+ <li>• "How do I get started?"</li>
540
+ </ul>
541
+ </div>
542
+ </div>
543
+ </div>
544
+ </div>
545
+ </div>
546
+
547
+ <!-- Input Area -->
548
+ <div class="input-area">
549
+ <div class="input-container">
550
+ <form id="chatForm">
551
+ <div class="input-wrapper">
552
+ <input
553
+ type="text"
554
+ class="message-input"
555
+ id="messageInput"
556
+ placeholder="Ask me anything about atomcamp..."
557
+ maxlength="1000"
558
+ autocomplete="off"
559
+ >
560
+ <button type="submit" class="send-button" id="sendButton">
561
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
562
+ <path d="M12 19V5M5 12l7-7 7 7"/>
563
+ </svg>
564
+ </button>
565
+ </div>
566
+ </form>
567
+ <div class="status-bar">
568
+ <span id="connectionStatus">Connected to atomcamp AI</span>
569
+ <span id="charCount">0/1000</span>
570
+ </div>
571
+ </div>
572
+ </div>
573
+
574
+ <script>
575
+ let messages = [];
576
+ let isTyping = false;
577
+ let isConnected = true;
578
+
579
+ const chatContainer = document.getElementById('chatContainer');
580
+ const welcomeScreen = document.getElementById('welcomeScreen');
581
+ const chatForm = document.getElementById('chatForm');
582
+ const messageInput = document.getElementById('messageInput');
583
+ const sendButton = document.getElementById('sendButton');
584
+ const status = document.getElementById('status');
585
+ const statusText = document.getElementById('status-text');
586
+ const connectionStatus = document.getElementById('connectionStatus');
587
+ const charCount = document.getElementById('charCount');
588
+
589
+ // Initialize
590
+ chatForm.addEventListener('submit', handleSubmit);
591
+ messageInput.addEventListener('input', updateCharCount);
592
+
593
+ function updateCharCount(e) {
594
+ const count = e.target.value.length;
595
+ charCount.textContent = `${count}/1000`;
596
+ }
597
+
598
+ async function handleSubmit(e) {
599
+ e.preventDefault();
600
+ const message = messageInput.value.trim();
601
+
602
+ if (!message || isTyping) return;
603
+
604
+ addMessage(message, 'user');
605
+ messageInput.value = '';
606
+ updateCharCount({ target: { value: '' } });
607
+ setTyping(true);
608
+
609
+ try {
610
+ // Simulate API call to your backend
611
+ const response = await fetch('/chat', {
612
+ method: 'POST',
613
+ headers: { 'Content-Type': 'application/json' },
614
+ body: JSON.stringify({ message })
615
+ });
616
+
617
+ const data = await response.json();
618
+
619
+ // Simulate typing delay
620
+ await new Promise(resolve => setTimeout(resolve, 1200));
621
+
622
+ addMessage(data.response || 'Sorry, I couldn\\'t generate a response.', 'bot');
623
+ setConnected(true);
624
+
625
+ } catch (error) {
626
+ console.error('Error:', error);
627
+ setConnected(false);
628
+ addMessage('I\\'m having trouble connecting. Please try again.', 'bot');
629
+ } finally {
630
+ setTyping(false);
631
+ }
632
+ }
633
+
634
+ function addMessage(content, role) {
635
+ if (messages.length === 0) {
636
+ welcomeScreen.style.display = 'none';
637
+ }
638
+
639
+ const messageDiv = document.createElement('div');
640
+ messageDiv.className = `message ${role}`;
641
+
642
+ const avatar = document.createElement('div');
643
+ avatar.className = `avatar ${role}`;
644
+ avatar.textContent = role === 'user' ? '👤' : '🤖';
645
+
646
+ const bubble = document.createElement('div');
647
+ bubble.className = 'message-bubble';
648
+
649
+ // Format message content
650
+ if (role === 'bot') {
651
+ bubble.innerHTML = formatMessageContent(content);
652
+ } else {
653
+ bubble.textContent = content;
654
+ }
655
+
656
+ if (role === 'user') {
657
+ messageDiv.appendChild(bubble);
658
+ messageDiv.appendChild(avatar);
659
+ } else {
660
+ messageDiv.appendChild(avatar);
661
+ messageDiv.appendChild(bubble);
662
+ }
663
+
664
+ chatContainer.appendChild(messageDiv);
665
+ scrollToBottom();
666
+
667
+ messages.push({ content, role, timestamp: new Date() });
668
+ }
669
+
670
+ function formatMessageContent(content) {
671
+ return content.split('\\n').map(line => {
672
+ if (line.trim().startsWith('•') || line.trim().startsWith('-')) {
673
+ return `<div style="display: flex; align-items: flex-start; margin-bottom: 6px;">
674
+ <span style="color: #16a34a; margin-right: 8px; margin-top: 2px; font-size: 0.875rem; font-weight: 500;">•</span>
675
+ <span style="font-size: 0.875rem; line-height: 1.5;">${line.replace(/^[•-]\\s*/, '')}</span>
676
+ </div>`;
677
+ } else if (/^\\d+\\./.test(line.trim())) {
678
+ const match = line.match(/^\\d+\\./);
679
+ return `<div style="display: flex; align-items: flex-start; margin-bottom: 6px;">
680
+ <span style="color: #16a34a; margin-right: 8px; font-weight: 600; font-size: 0.875rem;">${match ? match[0] : ''}</span>
681
+ <span style="font-size: 0.875rem; line-height: 1.5;">${line.replace(/^\\d+\\.\\s*/, '')}</span>
682
+ </div>`;
683
+ } else if (line.trim() === '') {
684
+ return '<br>';
685
+ } else {
686
+ return `<p style="margin-bottom: 6px; font-size: 0.875rem; line-height: 1.5;">${line}</p>`;
687
+ }
688
+ }).join('');
689
+ }
690
+
691
+ function setTyping(typing) {
692
+ isTyping = typing;
693
+ sendButton.disabled = typing;
694
+
695
+ if (typing) {
696
+ status.classList.add('typing');
697
+ statusText.innerHTML = `
698
+ <div class="typing-dots">
699
+ <div class="typing-dot"></div>
700
+ <div class="typing-dot"></div>
701
+ <div class="typing-dot"></div>
702
+ </div>
703
+ Typing...
704
+ `;
705
+ showTypingIndicator();
706
+ } else {
707
+ status.classList.remove('typing');
708
+ statusText.innerHTML = `<div class="status-dot"></div> ${isConnected ? 'Online' : 'Offline'}`;
709
+ hideTypingIndicator();
710
+ }
711
+ }
712
+
713
+ function showTypingIndicator() {
714
+ const typingDiv = document.createElement('div');
715
+ typingDiv.className = 'message bot';
716
+ typingDiv.id = 'typingIndicator';
717
+
718
+ const avatar = document.createElement('div');
719
+ avatar.className = 'avatar bot';
720
+ avatar.textContent = '🤖';
721
+
722
+ const indicator = document.createElement('div');
723
+ indicator.className = 'typing-indicator';
724
+ indicator.innerHTML = `
725
+ <div style="display: flex; gap: 4px;">
726
+ <div style="width: 8px; height: 8px; background: #9ca3af; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out;"></div>
727
+ <div style="width: 8px; height: 8px; background: #9ca3af; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; animation-delay: -0.32s;"></div>
728
+ <div style="width: 8px; height: 8px; background: #9ca3af; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; animation-delay: -0.16s;"></div>
729
+ </div>
730
+ <span style="font-size: 0.875rem; color: #6b7280; font-weight: 500;">typing...</span>
731
+ `;
732
+
733
+ typingDiv.appendChild(avatar);
734
+ typingDiv.appendChild(indicator);
735
+ chatContainer.appendChild(typingDiv);
736
+ scrollToBottom();
737
+ }
738
+
739
+ function hideTypingIndicator() {
740
+ const indicator = document.getElementById('typingIndicator');
741
+ if (indicator) indicator.remove();
742
+ }
743
+
744
+ function setConnected(connected) {
745
+ isConnected = connected;
746
+ connectionStatus.textContent = connected ? 'Connected to atomcamp AI' : 'Connection lost';
747
+
748
+ if (!isTyping) {
749
+ statusText.innerHTML = `<div class="status-dot"></div> ${connected ? 'Online' : 'Offline'}`;
750
+ }
751
+ }
752
+
753
+ function scrollToBottom() {
754
+ setTimeout(() => {
755
+ chatContainer.scrollTop = chatContainer.scrollHeight;
756
+ }, 100);
757
+ }
758
+ </script>
759
+ </body>
760
+ </html>
761
  """
762
+
763
+ def chat_response(message, history):
764
+ """Handle chat responses"""
765
  if not message.strip():
766
  return history, ""
767
 
768
  # Add user message to history
769
+ if history is None:
770
+ history = []
771
+
772
  history = history + [[message, None]]
773
 
774
  try:
775
  # Simulate typing delay
776
  time.sleep(1)
777
 
778
+ # Get relevant documents
779
+ if retriever:
780
+ docs = retriever.get_relevant_documents(message)
781
+ context = "\n\n".join([doc.page_content for doc in docs[:3]])
782
+ else:
783
+ context = "I'm an AI assistant for Atomcamp, a data science education platform."
784
 
785
+ # Generate response
786
+ response = generate_response(message, context)
 
787
 
788
  # Add bot response to history
789
  history[-1][1] = response
790
 
791
  except Exception as e:
792
+ history[-1][1] = f"Sorry, I encountered an error: {str(e)}. Please try again."
793
 
794
  return history, ""
795
 
796
+ # Create Gradio interface with custom HTML
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
797
  with gr.Blocks(
 
798
  title="atomcamp AI Chatbot",
799
+ css="""
800
+ .gradio-container { display: none !important; }
801
+ body { margin: 0 !important; padding: 0 !important; }
802
+ """,
803
+ head="""
804
+ <style>
805
+ .gradio-container { display: none !important; }
806
+ #root { display: none !important; }
807
+ </style>
808
+ """
809
  ) as demo:
810
 
811
+ # Hidden Gradio components for backend functionality
812
+ with gr.Row(visible=False):
813
+ chatbot_hidden = gr.Chatbot()
814
+ msg_hidden = gr.Textbox()
815
+ submit_hidden = gr.Button()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
 
817
+ # Custom HTML interface
818
+ gr.HTML(create_chat_interface())
 
 
 
 
 
 
 
819
 
820
+ # Backend API endpoint
821
+ @demo.api_name("chat")
822
+ def api_chat(message):
823
+ try:
824
+ # Get relevant documents
825
+ if retriever:
826
+ docs = retriever.get_relevant_documents(message)
827
+ context = "\n\n".join([doc.page_content for doc in docs[:3]])
828
+ else:
829
+ context = "I'm an AI assistant for Atomcamp."
830
+
831
+ # Generate response
832
+ response = generate_response(message, context)
833
+ return {"response": response}
834
+
835
+ except Exception as e:
836
+ return {"response": f"Sorry, I encountered an error: {str(e)}"}
837
 
838
+ # Launch configuration
839
  if __name__ == "__main__":
840
  demo.launch(
841
  server_name="0.0.0.0",
842
  server_port=7860,
843
+ share=True,
844
+ show_error=True
845
+ )