Ali Abdullah commited on
Commit
2851ee8
·
verified ·
1 Parent(s): 386945f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +721 -360
app.py CHANGED
@@ -1,10 +1,8 @@
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
@@ -13,7 +11,6 @@ import requests
13
  load_dotenv()
14
 
15
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
16
-
17
  vectorstore = None
18
  retriever = None
19
  groq_api_key = None
@@ -21,11 +18,7 @@ groq_api_key = None
21
  def initialize_groq():
22
  global groq_api_key
23
  groq_api_key = os.getenv("GROQ_API_KEY")
24
- if groq_api_key:
25
- print("Groq API key found")
26
- return "Groq API key found"
27
- print("Groq API key not found")
28
- return "Groq API key not found"
29
 
30
  def initialize_vectorstore():
31
  global vectorstore, retriever
@@ -33,7 +26,6 @@ def initialize_vectorstore():
33
  try:
34
  vectorstore = FAISS.load_local("atomcamp_vector_db", embeddings, allow_dangerous_deserialization=True)
35
  retriever = vectorstore.as_retriever()
36
- print("Vectorstore loaded successfully")
37
  return "Vectorstore loaded successfully"
38
  except:
39
  sample_data = [
@@ -48,21 +40,10 @@ def initialize_vectorstore():
48
  {
49
  "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.",
50
  "url": "https://www.atomcamp.com/learning-paths"
51
- },
52
- {
53
- "text": "Career services at Atomcamp include: Resume building, Interview preparation, Portfolio development, Job placement assistance, Industry networking events, Mentorship programs, and Career counseling sessions.",
54
- "url": "https://www.atomcamp.com/career-services"
55
- },
56
- {
57
- "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.",
58
- "url": "https://www.atomcamp.com/instructors"
59
  }
60
  ]
61
 
62
- docs = []
63
- for item in sample_data:
64
- docs.append(Document(page_content=item["text"], metadata={"url": item["url"]}))
65
-
66
  splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
67
  chunks = splitter.split_documents(docs)
68
 
@@ -70,7 +51,6 @@ def initialize_vectorstore():
70
  vectorstore.save_local("atomcamp_vector_db")
71
  retriever = vectorstore.as_retriever()
72
 
73
- print("Sample vectorstore created successfully")
74
  return "Sample vectorstore created successfully"
75
 
76
  def call_groq_api(message, context):
@@ -120,25 +100,21 @@ def call_groq_api(message, context):
120
  result = response.json()
121
  return result["choices"][0]["message"]["content"]
122
  else:
123
- print(f"Groq API error: {response.status_code}")
124
  return None
125
 
126
  except Exception as e:
127
- print(f"Groq API error: {e}")
128
  return None
129
 
130
- def generate_response_with_groq(message, context):
131
  groq_response = call_groq_api(message, context)
132
 
133
  if groq_response:
134
  return groq_response
135
- else:
136
- return generate_fallback_response(message, context)
137
-
138
- def generate_fallback_response(message, context):
139
  message_lower = message.lower()
140
 
141
- if any(word in message_lower for word in ['course', 'courses', 'learn', 'study', 'curriculum']):
142
  return """Atomcamp Courses:
143
 
144
  Core Programs:
@@ -155,8 +131,8 @@ Learning Tracks:
155
  • Advanced Track (9 months) - Industry-ready with job placement
156
 
157
  Would you like details about any specific course?"""
158
-
159
- elif any(word in message_lower for word in ['career', 'job', 'placement', 'salary', 'work']):
160
  return """Career Services at Atomcamp:
161
 
162
  Job Placement Support:
@@ -173,78 +149,8 @@ Career Growth:
173
  • Ongoing career mentorship
174
  • Industry networking events
175
 
176
- Success Stories:
177
- Our graduates work at top companies like Google, Microsoft, Amazon, Netflix, and leading startups worldwide.
178
-
179
  Ready to transform your career in data science?"""
180
-
181
- elif any(word in message_lower for word in ['about', 'atomcamp', 'company', 'who', 'what']):
182
- return """About Atomcamp:
183
-
184
- Atomcamp is a premier data science education platform dedicated to transforming careers through practical, industry-focused learning.
185
-
186
- What Makes Us Special:
187
- • Expert Instructors - Industry professionals with 10+ years experience
188
- • Hands-on Learning - Real projects, not just theory
189
- • Personalized Mentorship - 1-on-1 guidance throughout your journey
190
- • Job Guarantee - 95% placement rate with salary increases up to 300%
191
- • Flexible Learning - Online, part-time, and full-time options
192
-
193
- Our Mission:
194
- To make data science education accessible, practical, and career-focused, helping students become job-ready data scientists."""
195
-
196
- elif any(word in message_lower for word in ['machine learning', 'ml', 'algorithm', 'model']):
197
- return """Machine Learning at Atomcamp:
198
-
199
- Core ML Topics Covered:
200
- • Supervised Learning - Linear/Logistic Regression, Decision Trees, Random Forest
201
- • Unsupervised Learning - Clustering, PCA, Association Rules
202
- • Deep Learning - Neural Networks, CNN, RNN, LSTM
203
- • Natural Language Processing - Text analysis, sentiment analysis
204
- • Computer Vision - Image recognition, object detection
205
-
206
- Practical Projects:
207
- • Predictive analytics for business
208
- • Recommendation systems
209
- • Fraud detection models
210
- • Customer segmentation
211
- • Price prediction algorithms
212
-
213
- Want to dive deeper into any specific ML topic?"""
214
-
215
- elif any(word in message_lower for word in ['python', 'programming', 'code', 'coding']):
216
- return """Python for Data Science at Atomcamp:
217
-
218
- Python Fundamentals:
219
- • Variables, data types, and control structures
220
- • Functions, classes, and object-oriented programming
221
- • File handling and error management
222
- • Libraries and package management
223
-
224
- Data Science Libraries:
225
- • Pandas - Data manipulation and analysis
226
- • NumPy - Numerical computing
227
- • Matplotlib/Seaborn - Data visualization
228
- • Scikit-learn - Machine learning
229
- • TensorFlow/PyTorch - Deep learning
230
-
231
- Perfect for beginners and experienced programmers alike!"""
232
-
233
- elif any(word in message_lower for word in ['start', 'begin', 'beginner', 'new', 'how']):
234
- return """Getting Started with Atomcamp:
235
-
236
- Step 1: Choose Your Path
237
- • Complete Beginner? Start with Python Fundamentals
238
- • Some Programming Experience? Jump to Data Science Essentials
239
- • Want to Specialize? Pick Machine Learning or AI track
240
-
241
- Step 2: Learning Format
242
- • Self-paced Online - Learn at your own speed
243
- • Live Cohorts - Interactive sessions with peers
244
- • 1-on-1 Mentorship - Personalized guidance
245
-
246
- Ready to begin your data science journey?"""
247
-
248
  else:
249
  return f"""Thank you for your question about "{message}"!
250
 
@@ -259,296 +165,751 @@ Would you like me to elaborate on any specific aspect?"""
259
 
260
  def chat_response(message):
261
  try:
262
- print(f"Received message: {message}")
263
-
264
  if retriever:
265
  docs = retriever.get_relevant_documents(message)
266
  context = "\n\n".join([doc.page_content for doc in docs[:3]])
267
  else:
268
  context = "I'm an AI assistant for Atomcamp, a data science education platform."
269
 
270
- response = generate_response_with_groq(message, context)
271
- print(f"Generated response: {response[:100]}...")
272
  return response
273
 
274
  except Exception as e:
275
- error_msg = f"Sorry, I encountered an error: {str(e)}. Please try again."
276
- print(f"Error in chat_response: {error_msg}")
277
- return error_msg
278
 
279
  # Initialize systems
280
- print("Initializing systems...")
281
- groq_status = initialize_groq()
282
- vectorstore_status = initialize_vectorstore()
283
- print(f"Groq: {groq_status}")
284
- print(f"Vectorstore: {vectorstore_status}")
285
-
286
- # Custom CSS and HTML
287
- custom_css = """
288
- body {
289
- font-family: 'Inter', sans-serif;
290
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
291
- margin: 0;
292
- padding: 0;
293
- }
294
-
295
- .gradio-container {
296
- max-width: none !important;
297
- margin: 0 !important;
298
- padding: 0 !important;
299
- }
300
-
301
- .main {
302
- max-width: none !important;
303
- margin: 0 !important;
304
- padding: 0 !important;
305
- }
306
-
307
- #chatbot-interface {
308
- min-height: 100vh;
309
- display: flex;
310
- flex-direction: column;
311
- }
312
-
313
- .header {
314
- text-align: center;
315
- padding: 2rem 1rem;
316
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
317
- }
318
-
319
- .logo {
320
- width: 160px;
321
- height: 48px;
322
- margin: 0 auto 1.5rem;
323
- background: #22c55e;
324
- border-radius: 24px;
325
- display: flex;
326
- align-items: center;
327
- justify-content: center;
328
- color: white;
329
- font-weight: 700;
330
- font-size: 18px;
331
- }
332
-
333
- .title-bubble {
334
- background: #f0fdf4;
335
- border: 1px solid #bbf7d0;
336
- border-radius: 1rem;
337
- padding: 1.5rem;
338
- display: inline-block;
339
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
340
- max-width: fit-content;
341
- }
342
-
343
- .title {
344
- font-size: 1.5rem;
345
- font-weight: 600;
346
- color: #166534;
347
- margin-bottom: 0.5rem;
348
- text-align: left;
349
- }
350
-
351
- .status {
352
- display: flex;
353
- align-items: center;
354
- gap: 0.5rem;
355
- font-size: 0.875rem;
356
- font-weight: 500;
357
- text-align: left;
358
- color: #166534;
359
- }
360
-
361
- .status-dot {
362
- width: 8px;
363
- height: 8px;
364
- border-radius: 50%;
365
- background: #22c55e;
366
- animation: pulse 2s infinite;
367
- }
368
-
369
- @keyframes pulse {
370
- 0%, 100% { opacity: 1; }
371
- 50% { opacity: 0.5; }
372
- }
373
-
374
- .welcome-section {
375
- flex: 1;
376
- display: flex;
377
- align-items: center;
378
- justify-content: center;
379
- padding: 2rem;
380
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
381
- }
382
-
383
- .welcome-card {
384
- background: white;
385
- border-radius: 1rem;
386
- padding: 2rem;
387
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
388
- max-width: 600px;
389
- text-align: center;
390
- }
391
-
392
- .welcome-icon {
393
- width: 48px;
394
- height: 48px;
395
- background: #22c55e;
396
- border-radius: 12px;
397
- display: flex;
398
- align-items: center;
399
- justify-content: center;
400
- margin: 0 auto 1rem;
401
- color: white;
402
- font-size: 24px;
403
- font-weight: bold;
404
- }
405
-
406
- .welcome-title {
407
- font-size: 1.25rem;
408
- font-weight: 600;
409
- margin-bottom: 1rem;
410
- color: #374151;
411
- }
412
-
413
- .welcome-description {
414
- color: #6b7280;
415
- margin-bottom: 2rem;
416
- font-size: 0.875rem;
417
- line-height: 1.5;
418
- }
419
-
420
- .feature-grid {
421
- display: grid;
422
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
423
- gap: 1rem;
424
- text-align: left;
425
- }
426
-
427
- .feature-card {
428
- background: #f9fafb;
429
- padding: 1rem;
430
- border-radius: 0.5rem;
431
- }
432
-
433
- .feature-title {
434
- font-weight: 600;
435
- margin-bottom: 0.5rem;
436
- font-size: 0.875rem;
437
- color: #374151;
438
- }
439
-
440
- .feature-list {
441
- font-size: 0.75rem;
442
- color: #6b7280;
443
- line-height: 1.5;
444
- list-style: none;
445
- padding: 0;
446
- margin: 0;
447
- }
448
-
449
- .feature-list li {
450
- margin-bottom: 0.25rem;
451
- }
452
- """
453
 
454
  def create_interface():
455
- with gr.Blocks(
456
- css=custom_css,
457
- title="atomcamp AI Chatbot",
458
- theme=gr.themes.Soft()
459
- ) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
 
461
- gr.HTML("""
462
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
463
- <div id="chatbot-interface">
464
- <div class="header">
465
- <div class="logo">atomcamp</div>
466
- <div class="title-bubble">
467
- <div class="title">Chatbot</div>
468
- <div class="status">
 
 
 
 
 
 
 
469
  <div class="status-dot"></div>
470
- <span>Online & Ready</span>
471
  </div>
472
  </div>
473
  </div>
474
-
475
- <div class="welcome-section">
476
- <div class="welcome-card">
477
- <div class="welcome-icon">AI</div>
478
- <h2 class="welcome-title">Welcome to atomcamp AI</h2>
479
- <p class="welcome-description">Ask me about courses, data science concepts, and learning paths.</p>
480
-
481
- <div class="feature-grid">
482
- <div class="feature-card">
483
- <h3 class="feature-title">Ask me about:</h3>
484
- <ul class="feature-list">
485
- <li>• Course information</li>
486
- <li>• Data science concepts</li>
487
- <li>• Learning paths</li>
488
- <li>• Career guidance</li>
489
- </ul>
490
- </div>
491
- <div class="feature-card">
492
- <h3 class="feature-title">Try asking:</h3>
493
- <ul class="feature-list">
494
- <li>• "What courses do you offer?"</li>
495
- <li>• "Explain machine learning"</li>
496
- <li>• "How do I get started?"</li>
497
- <li>• "Career opportunities?"</li>
498
- </ul>
499
- </div>
500
  </div>
501
  </div>
502
  </div>
503
  </div>
504
- """)
505
 
506
- with gr.Row():
507
- with gr.Column(scale=1):
508
- chatbot = gr.Chatbot(
509
- label="Chat with atomcamp AI",
510
- height=400,
511
- show_label=True,
512
- container=True,
513
- type="messages"
514
- )
 
 
 
 
 
 
 
 
 
 
 
 
515
 
516
- with gr.Row():
517
- msg = gr.Textbox(
518
- placeholder="Ask me anything about atomcamp...",
519
- container=False,
520
- scale=4,
521
- max_lines=3
522
- )
523
- submit = gr.Button(
524
- "Send",
525
- variant="primary",
526
- scale=1
527
- )
528
-
529
- def respond(message, history):
530
- if not message.strip():
531
- return history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
- # Add user message to history
534
- history.append({"role": "user", "content": message})
535
 
536
- # Get bot response
537
- bot_response = chat_response(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
538
 
539
- # Add bot response to history
540
- history.append({"role": "assistant", "content": bot_response})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
 
542
- return history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
 
544
- # Set up event handlers
545
- submit.click(respond, [msg, chatbot], [chatbot, msg])
546
- msg.submit(respond, [msg, chatbot], [chatbot, msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
547
 
548
  return demo
549
 
550
  if __name__ == "__main__":
551
- print("Starting atomcamp AI Chatbot...")
552
  demo = create_interface()
553
  demo.launch(
554
  server_name="0.0.0.0",
 
1
  import gradio as gr
2
  import os
3
  from dotenv import load_dotenv
 
4
  from langchain_community.vectorstores import FAISS
5
  from langchain_huggingface import HuggingFaceEmbeddings
 
6
  from langchain.text_splitter import RecursiveCharacterTextSplitter
7
  from langchain_core.documents import Document
8
  import time
 
11
  load_dotenv()
12
 
13
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
 
14
  vectorstore = None
15
  retriever = None
16
  groq_api_key = None
 
18
  def initialize_groq():
19
  global groq_api_key
20
  groq_api_key = os.getenv("GROQ_API_KEY")
21
+ return "Groq API key found" if groq_api_key else "Groq API key not found"
 
 
 
 
22
 
23
  def initialize_vectorstore():
24
  global vectorstore, retriever
 
26
  try:
27
  vectorstore = FAISS.load_local("atomcamp_vector_db", embeddings, allow_dangerous_deserialization=True)
28
  retriever = vectorstore.as_retriever()
 
29
  return "Vectorstore loaded successfully"
30
  except:
31
  sample_data = [
 
40
  {
41
  "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.",
42
  "url": "https://www.atomcamp.com/learning-paths"
 
 
 
 
 
 
 
 
43
  }
44
  ]
45
 
46
+ docs = [Document(page_content=item["text"], metadata={"url": item["url"]}) for item in sample_data]
 
 
 
47
  splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
48
  chunks = splitter.split_documents(docs)
49
 
 
51
  vectorstore.save_local("atomcamp_vector_db")
52
  retriever = vectorstore.as_retriever()
53
 
 
54
  return "Sample vectorstore created successfully"
55
 
56
  def call_groq_api(message, context):
 
100
  result = response.json()
101
  return result["choices"][0]["message"]["content"]
102
  else:
 
103
  return None
104
 
105
  except Exception as e:
 
106
  return None
107
 
108
+ def generate_response(message, context):
109
  groq_response = call_groq_api(message, context)
110
 
111
  if groq_response:
112
  return groq_response
113
+
114
+ # Fallback responses
 
 
115
  message_lower = message.lower()
116
 
117
+ if any(word in message_lower for word in ['course', 'courses', 'learn', 'study']):
118
  return """Atomcamp Courses:
119
 
120
  Core Programs:
 
131
  • Advanced Track (9 months) - Industry-ready with job placement
132
 
133
  Would you like details about any specific course?"""
134
+
135
+ elif any(word in message_lower for word in ['career', 'job', 'placement']):
136
  return """Career Services at Atomcamp:
137
 
138
  Job Placement Support:
 
149
  • Ongoing career mentorship
150
  • Industry networking events
151
 
 
 
 
152
  Ready to transform your career in data science?"""
153
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  else:
155
  return f"""Thank you for your question about "{message}"!
156
 
 
165
 
166
  def chat_response(message):
167
  try:
 
 
168
  if retriever:
169
  docs = retriever.get_relevant_documents(message)
170
  context = "\n\n".join([doc.page_content for doc in docs[:3]])
171
  else:
172
  context = "I'm an AI assistant for Atomcamp, a data science education platform."
173
 
174
+ response = generate_response(message, context)
 
175
  return response
176
 
177
  except Exception as e:
178
+ return f"Sorry, I encountered an error: {str(e)}. Please try again."
 
 
179
 
180
  # Initialize systems
181
+ initialize_groq()
182
+ initialize_vectorstore()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  def create_interface():
185
+ interface_html = """
186
+ <!DOCTYPE html>
187
+ <html lang="en">
188
+ <head>
189
+ <meta charset="UTF-8">
190
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
191
+ <title>atomcamp AI Chatbot</title>
192
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
193
+ <style>
194
+ * {
195
+ margin: 0;
196
+ padding: 0;
197
+ box-sizing: border-box;
198
+ }
199
+
200
+ body {
201
+ font-family: 'Inter', sans-serif;
202
+ background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
203
+ min-height: 100vh;
204
+ display: flex;
205
+ flex-direction: column;
206
+ }
207
+
208
+ .header {
209
+ display: flex;
210
+ flex-direction: column;
211
+ align-items: center;
212
+ padding-top: 2rem;
213
+ padding-bottom: 1.5rem;
214
+ }
215
+
216
+ .logo {
217
+ margin-bottom: 1.5rem;
218
+ }
219
+
220
+ .logo-img {
221
+ height: 3rem;
222
+ width: auto;
223
+ object-fit: contain;
224
+ }
225
+
226
+ .logo-placeholder {
227
+ width: 160px;
228
+ height: 48px;
229
+ background: #22c55e;
230
+ border-radius: 24px;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ color: white;
235
+ font-weight: 700;
236
+ font-size: 18px;
237
+ }
238
+
239
+ .title-bubble {
240
+ width: 100%;
241
+ max-width: 64rem;
242
+ padding: 0 1rem;
243
+ }
244
+
245
+ .bubble-container {
246
+ background: #f0fdf4;
247
+ border: 1px solid #e5e7eb;
248
+ border-radius: 1rem;
249
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
250
+ padding: 1.5rem;
251
+ max-width: fit-content;
252
+ }
253
+
254
+ .bubble-content {
255
+ text-align: left;
256
+ }
257
+
258
+ .chatbot-title {
259
+ font-size: 1.5rem;
260
+ font-weight: 600;
261
+ color: #166534;
262
+ margin-bottom: 0.25rem;
263
+ letter-spacing: -0.025em;
264
+ }
265
+
266
+ .typing-indicator {
267
+ display: flex;
268
+ align-items: center;
269
+ gap: 0.5rem;
270
+ }
271
+
272
+ .typing-dots {
273
+ display: flex;
274
+ gap: 0.25rem;
275
+ }
276
+
277
+ .typing-dot {
278
+ width: 0.375rem;
279
+ height: 0.375rem;
280
+ background: #166534;
281
+ border-radius: 50%;
282
+ animation: bounce 1s infinite;
283
+ }
284
+
285
+ .typing-dot:nth-child(2) {
286
+ animation-delay: 0.1s;
287
+ }
288
+
289
+ .typing-dot:nth-child(3) {
290
+ animation-delay: 0.2s;
291
+ }
292
+
293
+ .typing-text {
294
+ color: #166534;
295
+ font-size: 1rem;
296
+ font-weight: 500;
297
+ }
298
+
299
+ .status-indicator {
300
+ display: flex;
301
+ align-items: center;
302
+ gap: 0.5rem;
303
+ }
304
+
305
+ .status-dot {
306
+ width: 0.5rem;
307
+ height: 0.5rem;
308
+ border-radius: 50%;
309
+ background: #16a34a;
310
+ }
311
+
312
+ .status-text {
313
+ font-size: 0.875rem;
314
+ font-weight: 500;
315
+ color: #166534;
316
+ }
317
+
318
+ .chat-container {
319
+ max-width: 64rem;
320
+ margin: 0 auto;
321
+ padding: 0 1rem;
322
+ padding-bottom: 8rem;
323
+ flex: 1;
324
+ }
325
+
326
+ .welcome-screen {
327
+ text-align: center;
328
+ padding: 3rem 0;
329
+ }
330
+
331
+ .welcome-card {
332
+ background: white;
333
+ border-radius: 1rem;
334
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
335
+ padding: 2rem;
336
+ max-width: 36rem;
337
+ margin: 0 auto;
338
+ }
339
+
340
+ .welcome-icon {
341
+ width: 3rem;
342
+ height: 3rem;
343
+ background: #22c55e;
344
+ border-radius: 0.75rem;
345
+ display: flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+ margin: 0 auto 1rem;
349
+ color: white;
350
+ font-size: 1.5rem;
351
+ font-weight: bold;
352
+ }
353
+
354
+ .welcome-title {
355
+ font-size: 1.25rem;
356
+ font-weight: 600;
357
+ color: #1f2937;
358
+ margin-bottom: 0.75rem;
359
+ letter-spacing: -0.025em;
360
+ }
361
+
362
+ .welcome-description {
363
+ color: #6b7280;
364
+ font-size: 0.875rem;
365
+ line-height: 1.5;
366
+ margin-bottom: 1.5rem;
367
+ }
368
+
369
+ .feature-grid {
370
+ display: grid;
371
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
372
+ gap: 0.75rem;
373
+ text-align: left;
374
+ }
375
+
376
+ .feature-card {
377
+ background: #f9fafb;
378
+ border-radius: 0.5rem;
379
+ padding: 0.75rem;
380
+ }
381
+
382
+ .feature-title {
383
+ font-weight: 600;
384
+ color: #1f2937;
385
+ margin-bottom: 0.5rem;
386
+ font-size: 0.875rem;
387
+ }
388
+
389
+ .feature-list {
390
+ font-size: 0.75rem;
391
+ color: #6b7280;
392
+ line-height: 1.5;
393
+ list-style: none;
394
+ padding: 0;
395
+ margin: 0;
396
+ }
397
+
398
+ .feature-list li {
399
+ margin-bottom: 0.25rem;
400
+ }
401
+
402
+ .messages-container {
403
+ display: flex;
404
+ flex-direction: column;
405
+ gap: 1rem;
406
+ }
407
+
408
+ .message {
409
+ display: flex;
410
+ animation: fadeIn 0.3s ease-out;
411
+ }
412
+
413
+ .message.user {
414
+ justify-content: flex-end;
415
+ }
416
+
417
+ .message.assistant {
418
+ justify-content: flex-start;
419
+ }
420
+
421
+ .message-content {
422
+ display: flex;
423
+ align-items: flex-end;
424
+ gap: 0.5rem;
425
+ max-width: 48rem;
426
+ }
427
+
428
+ .message.user .message-content {
429
+ max-width: 28rem;
430
+ }
431
+
432
+ .avatar {
433
+ width: 2rem;
434
+ height: 2rem;
435
+ border-radius: 50%;
436
+ display: flex;
437
+ align-items: center;
438
+ justify-content: center;
439
+ flex-shrink: 0;
440
+ }
441
+
442
+ .avatar.user {
443
+ background: #22c55e;
444
+ color: white;
445
+ }
446
+
447
+ .avatar.assistant {
448
+ background: #e5e7eb;
449
+ color: #6b7280;
450
+ }
451
+
452
+ .message-bubble {
453
+ padding: 0.75rem 1rem;
454
+ border-radius: 1rem;
455
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
456
+ }
457
+
458
+ .message.user .message-bubble {
459
+ background: #22c55e;
460
+ color: white;
461
+ border-bottom-right-radius: 0.375rem;
462
+ }
463
+
464
+ .message.assistant .message-bubble {
465
+ background: #e5e7eb;
466
+ color: #1f2937;
467
+ border-bottom-left-radius: 0.375rem;
468
+ }
469
+
470
+ .message-text {
471
+ font-size: 0.875rem;
472
+ font-weight: 500;
473
+ line-height: 1.5;
474
+ }
475
+
476
+ .message-formatted {
477
+ font-size: 0.875rem;
478
+ line-height: 1.5;
479
+ }
480
+
481
+ .bullet-point {
482
+ display: flex;
483
+ align-items: flex-start;
484
+ margin-bottom: 0.375rem;
485
+ }
486
+
487
+ .bullet-icon {
488
+ color: #16a34a;
489
+ margin-right: 0.5rem;
490
+ margin-top: 0.125rem;
491
+ font-size: 0.875rem;
492
+ font-weight: 500;
493
+ }
494
+
495
+ .bullet-text {
496
+ font-size: 0.875rem;
497
+ line-height: 1.5;
498
+ }
499
+
500
+ .numbered-point {
501
+ display: flex;
502
+ align-items: flex-start;
503
+ margin-bottom: 0.375rem;
504
+ }
505
+
506
+ .number-icon {
507
+ color: #16a34a;
508
+ margin-right: 0.5rem;
509
+ font-weight: 600;
510
+ font-size: 0.875rem;
511
+ }
512
+
513
+ .input-area {
514
+ position: fixed;
515
+ bottom: 0;
516
+ left: 0;
517
+ right: 0;
518
+ background: rgba(255, 255, 255, 0.95);
519
+ backdrop-filter: blur(8px);
520
+ border-top: 1px solid #e5e7eb;
521
+ padding: 1rem;
522
+ }
523
+
524
+ .input-container {
525
+ max-width: 64rem;
526
+ margin: 0 auto;
527
+ }
528
+
529
+ .input-form {
530
+ position: relative;
531
+ }
532
+
533
+ .input-wrapper {
534
+ position: relative;
535
+ background: #4b5563;
536
+ border-radius: 9999px;
537
+ overflow: hidden;
538
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
539
+ }
540
+
541
+ .message-input {
542
+ width: 100%;
543
+ padding: 0.875rem 1.25rem;
544
+ padding-right: 3rem;
545
+ background: transparent;
546
+ border: none;
547
+ outline: none;
548
+ color: white;
549
+ font-size: 0.875rem;
550
+ font-weight: 500;
551
+ letter-spacing: 0.025em;
552
+ }
553
+
554
+ .message-input::placeholder {
555
+ color: #d1d5db;
556
+ }
557
+
558
+ .send-button {
559
+ position: absolute;
560
+ right: 0.5rem;
561
+ top: 50%;
562
+ transform: translateY(-50%);
563
+ background: #22c55e;
564
+ color: white;
565
+ border: none;
566
+ border-radius: 50%;
567
+ width: 2.5rem;
568
+ height: 2.5rem;
569
+ display: flex;
570
+ align-items: center;
571
+ justify-content: center;
572
+ cursor: pointer;
573
+ transition: all 0.2s;
574
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
575
+ }
576
+
577
+ .send-button:hover:not(:disabled) {
578
+ background: #16a34a;
579
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
580
+ }
581
+
582
+ .send-button:disabled {
583
+ opacity: 0.5;
584
+ cursor: not-allowed;
585
+ }
586
+
587
+ .status-bar {
588
+ display: flex;
589
+ justify-content: space-between;
590
+ align-items: center;
591
+ margin-top: 0.5rem;
592
+ padding: 0 0.5rem;
593
+ }
594
+
595
+ .connection-status {
596
+ font-size: 0.75rem;
597
+ font-weight: 500;
598
+ color: #6b7280;
599
+ }
600
+
601
+ .char-count {
602
+ font-size: 0.75rem;
603
+ font-weight: 500;
604
+ color: #9ca3af;
605
+ }
606
+
607
+ @keyframes bounce {
608
+ 0%, 80%, 100% {
609
+ transform: scale(0);
610
+ }
611
+ 40% {
612
+ transform: scale(1);
613
+ }
614
+ }
615
+
616
+ @keyframes fadeIn {
617
+ from {
618
+ opacity: 0;
619
+ transform: translateY(8px);
620
+ }
621
+ to {
622
+ opacity: 1;
623
+ transform: translateY(0);
624
+ }
625
+ }
626
+
627
+ .hidden {
628
+ display: none;
629
+ }
630
+
631
+ @media (max-width: 768px) {
632
+ .header {
633
+ padding-top: 1rem;
634
+ padding-bottom: 1rem;
635
+ }
636
+
637
+ .chat-container {
638
+ padding-bottom: 6rem;
639
+ }
640
+
641
+ .message-content {
642
+ max-width: calc(100vw - 2rem);
643
+ }
644
+
645
+ .message.user .message-content {
646
+ max-width: calc(100vw - 2rem);
647
+ }
648
+ }
649
+ </style>
650
+ </head>
651
+ <body>
652
+ <div class="header">
653
+ <div class="logo">
654
+ <div class="logo-placeholder">atomcamp</div>
655
+ </div>
656
 
657
+ <div class="title-bubble">
658
+ <div class="bubble-container">
659
+ <div class="bubble-content">
660
+ <h1 class="chatbot-title">Chatbot</h1>
661
+
662
+ <div id="typingIndicator" class="typing-indicator hidden">
663
+ <div class="typing-dots">
664
+ <div class="typing-dot"></div>
665
+ <div class="typing-dot"></div>
666
+ <div class="typing-dot"></div>
667
+ </div>
668
+ <span class="typing-text">Typing...</span>
669
+ </div>
670
+
671
+ <div id="statusIndicator" class="status-indicator">
672
  <div class="status-dot"></div>
673
+ <span class="status-text">Online</span>
674
  </div>
675
  </div>
676
  </div>
677
+ </div>
678
+ </div>
679
+
680
+ <div class="chat-container">
681
+ <div id="welcomeScreen" class="welcome-screen">
682
+ <div class="welcome-card">
683
+ <div class="welcome-icon">✨</div>
684
+ <h2 class="welcome-title">Welcome to atomcamp AI</h2>
685
+ <p class="welcome-description">Ask me about courses, data science concepts, and learning paths.</p>
686
+
687
+ <div class="feature-grid">
688
+ <div class="feature-card">
689
+ <h3 class="feature-title">Ask me about:</h3>
690
+ <ul class="feature-list">
691
+ <li>• Course information</li>
692
+ <li>• Data science concepts</li>
693
+ <li>• Learning paths</li>
694
+ </ul>
695
+ </div>
696
+ <div class="feature-card">
697
+ <h3 class="feature-title">Try asking:</h3>
698
+ <ul class="feature-list">
699
+ <li>• "What courses do you offer?"</li>
700
+ <li>• "Explain machine learning"</li>
701
+ <li>• "How do I get started?"</li>
702
+ </ul>
703
  </div>
704
  </div>
705
  </div>
706
  </div>
 
707
 
708
+ <div id="messagesContainer" class="messages-container hidden"></div>
709
+ </div>
710
+
711
+ <div class="input-area">
712
+ <div class="input-container">
713
+ <form id="chatForm" class="input-form">
714
+ <div class="input-wrapper">
715
+ <input
716
+ type="text"
717
+ id="messageInput"
718
+ class="message-input"
719
+ placeholder="Ask me anything about atomcamp..."
720
+ maxlength="1000"
721
+ autocomplete="off"
722
+ >
723
+ <button type="submit" id="sendButton" class="send-button">
724
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
725
+ <path d="M12 19V5M5 12l7-7 7 7"/>
726
+ </svg>
727
+ </button>
728
+ </div>
729
 
730
+ <div class="status-bar">
731
+ <span id="connectionStatus" class="connection-status">Connected to atomcamp AI</span>
732
+ <span id="charCount" class="char-count">0/1000</span>
733
+ </div>
734
+ </form>
735
+ </div>
736
+ </div>
737
+
738
+ <script>
739
+ let messages = [];
740
+ let isTyping = false;
741
+ let isConnected = true;
742
+
743
+ const welcomeScreen = document.getElementById('welcomeScreen');
744
+ const messagesContainer = document.getElementById('messagesContainer');
745
+ const chatForm = document.getElementById('chatForm');
746
+ const messageInput = document.getElementById('messageInput');
747
+ const sendButton = document.getElementById('sendButton');
748
+ const typingIndicator = document.getElementById('typingIndicator');
749
+ const statusIndicator = document.getElementById('statusIndicator');
750
+ const connectionStatus = document.getElementById('connectionStatus');
751
+ const charCount = document.getElementById('charCount');
752
+
753
+ chatForm.addEventListener('submit', handleSubmit);
754
+ messageInput.addEventListener('input', updateCharCount);
755
+
756
+ function updateCharCount() {
757
+ const count = messageInput.value.length;
758
+ charCount.textContent = `${count}/1000`;
759
+ }
760
+
761
+ async function handleSubmit(e) {
762
+ e.preventDefault();
763
+ const message = messageInput.value.trim();
764
+
765
+ if (!message || isTyping) return;
766
+
767
+ addMessage(message, 'user');
768
+ messageInput.value = '';
769
+ updateCharCount();
770
+ setTyping(true);
771
+
772
+ try {
773
+ const response = await fetch('/predict', {
774
+ method: 'POST',
775
+ headers: { 'Content-Type': 'application/json' },
776
+ body: JSON.stringify({ data: [message] })
777
+ });
778
+
779
+ const data = await response.json();
780
+
781
+ await new Promise(resolve => setTimeout(resolve, 1200));
782
+
783
+ addMessage(data.data[0] || 'Sorry, I could not generate a response.', 'assistant');
784
+ setConnected(true);
785
+
786
+ } catch (error) {
787
+ console.error('Error:', error);
788
+ setConnected(false);
789
+ addMessage('I am having trouble connecting. Please try again.', 'assistant');
790
+ } finally {
791
+ setTyping(false);
792
+ }
793
+ }
794
+
795
+ function addMessage(content, role) {
796
+ if (messages.length === 0) {
797
+ welcomeScreen.classList.add('hidden');
798
+ messagesContainer.classList.remove('hidden');
799
+ }
800
+
801
+ const messageDiv = document.createElement('div');
802
+ messageDiv.className = `message ${role}`;
803
+
804
+ const messageContent = document.createElement('div');
805
+ messageContent.className = 'message-content';
806
 
807
+ const avatar = document.createElement('div');
808
+ avatar.className = `avatar ${role}`;
809
 
810
+ if (role === 'user') {
811
+ avatar.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>';
812
+ } else {
813
+ avatar.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><circle cx="12" cy="5" r="2"></circle><path d="M12 7v4"></path><line x1="8" y1="16" x2="8" y2="16"></line><line x1="16" y1="16" x2="16" y2="16"></line></svg>';
814
+ }
815
+
816
+ const bubble = document.createElement('div');
817
+ bubble.className = 'message-bubble';
818
+
819
+ if (role === 'user') {
820
+ bubble.innerHTML = `<div class="message-text">${content}</div>`;
821
+ messageContent.appendChild(bubble);
822
+ messageContent.appendChild(avatar);
823
+ } else {
824
+ bubble.innerHTML = `<div class="message-formatted">${formatMessageContent(content)}</div>`;
825
+ messageContent.appendChild(avatar);
826
+ messageContent.appendChild(bubble);
827
+ }
828
 
829
+ messageDiv.appendChild(messageContent);
830
+ messagesContainer.appendChild(messageDiv);
831
+
832
+ scrollToBottom();
833
+ messages.push({ content, role, timestamp: new Date() });
834
+ }
835
+
836
+ function formatMessageContent(content) {
837
+ return content.split('\\n').map(line => {
838
+ if (line.trim().startsWith('•') || line.trim().startsWith('-')) {
839
+ return `<div class="bullet-point">
840
+ <span class="bullet-icon">•</span>
841
+ <span class="bullet-text">${line.replace(/^[•-]\\s*/, '')}</span>
842
+ </div>`;
843
+ } else if (/^\\d+\\./.test(line.trim())) {
844
+ const match = line.match(/^\\d+\\./);
845
+ return `<div class="numbered-point">
846
+ <span class="number-icon">${match ? match[0] : ''}</span>
847
+ <span class="bullet-text">${line.replace(/^\\d+\\.\\s*/, '')}</span>
848
+ </div>`;
849
+ } else if (line.trim() === '') {
850
+ return '<br>';
851
+ } else {
852
+ return `<p style="margin-bottom: 0.375rem; font-size: 0.875rem; line-height: 1.5;">${line}</p>`;
853
+ }
854
+ }).join('');
855
+ }
856
+
857
+ function setTyping(typing) {
858
+ isTyping = typing;
859
+ sendButton.disabled = typing;
860
 
861
+ if (typing) {
862
+ typingIndicator.classList.remove('hidden');
863
+ statusIndicator.classList.add('hidden');
864
+ } else {
865
+ typingIndicator.classList.add('hidden');
866
+ statusIndicator.classList.remove('hidden');
867
+ }
868
+ }
869
+
870
+ function setConnected(connected) {
871
+ isConnected = connected;
872
+ connectionStatus.textContent = connected ? 'Connected to atomcamp AI' : 'Connection lost';
873
+ }
874
+
875
+ function scrollToBottom() {
876
+ setTimeout(() => {
877
+ window.scrollTo({
878
+ top: document.body.scrollHeight,
879
+ behavior: 'smooth'
880
+ });
881
+ }, 100);
882
+ }
883
+ </script>
884
+ </body>
885
+ </html>
886
+ """
887
+
888
+ with gr.Blocks(
889
+ css=".gradio-container { display: none !important; }",
890
+ title="atomcamp AI Chatbot"
891
+ ) as demo:
892
 
893
+ # Hidden components for API
894
+ with gr.Row(visible=False):
895
+ input_text = gr.Textbox()
896
+ output_text = gr.Textbox()
897
+ submit_btn = gr.Button()
898
+
899
+ # Display the interface
900
+ gr.HTML(interface_html)
901
+
902
+ # Set up API endpoint
903
+ submit_btn.click(
904
+ fn=chat_response,
905
+ inputs=input_text,
906
+ outputs=output_text,
907
+ api_name="predict"
908
+ )
909
 
910
  return demo
911
 
912
  if __name__ == "__main__":
 
913
  demo = create_interface()
914
  demo.launch(
915
  server_name="0.0.0.0",