Ali Abdullah commited on
Commit
3d9f649
·
verified ·
1 Parent(s): f8006dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -733
app.py CHANGED
@@ -24,7 +24,7 @@ def initialize_groq():
24
 
25
  def initialize_vectorstore():
26
  global vectorstore, retriever
27
-
28
  try:
29
  vectorstore = FAISS.load_local("atomcamp_vector_db", embeddings, allow_dangerous_deserialization=True)
30
  retriever = vectorstore.as_retriever()
@@ -44,29 +44,29 @@ def initialize_vectorstore():
44
  "url": "https://www.atomcamp.com/learning-paths"
45
  }
46
  ]
47
-
48
  docs = [Document(page_content=item["text"], metadata={"url": item["url"]}) for item in sample_data]
49
  splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
50
  chunks = splitter.split_documents(docs)
51
-
52
  vectorstore = FAISS.from_documents(chunks, embeddings)
53
  vectorstore.save_local("atomcamp_vector_db")
54
  retriever = vectorstore.as_retriever()
55
-
56
  return "Sample vectorstore created successfully"
57
 
58
  def call_groq_api(message, context):
59
  global groq_api_key
60
-
61
  if not groq_api_key:
62
  return None
63
-
64
  try:
65
  system_prompt = f"""You are an AI assistant for Atomcamp, a data science education platform.
66
  Use the following context to answer questions about Atomcamp's courses, career services, and data science topics.
67
-
68
  Context: {context}
69
-
70
  Guidelines:
71
  - Be helpful and informative
72
  - Focus on Atomcamp's offerings
@@ -75,12 +75,12 @@ def call_groq_api(message, context):
75
  - Keep responses concise but comprehensive
76
  - Do not use emojis
77
  """
78
-
79
  headers = {
80
  "Authorization": f"Bearer {groq_api_key}",
81
  "Content-Type": "application/json"
82
  }
83
-
84
  data = {
85
  "messages": [
86
  {"role": "system", "content": system_prompt},
@@ -90,769 +90,101 @@ def call_groq_api(message, context):
90
  "temperature": 0.7,
91
  "max_tokens": 1000
92
  }
93
-
94
  response = requests.post(
95
  "https://api.groq.com/openai/v1/chat/completions",
96
  headers=headers,
97
  json=data,
98
  timeout=30
99
  )
100
-
101
  if response.status_code == 200:
102
  result = response.json()
103
  return result["choices"][0]["message"]["content"]
104
  else:
105
  return None
106
-
107
  except Exception as e:
108
  return None
109
 
110
  def generate_response(message, context):
111
  groq_response = call_groq_api(message, context)
112
-
113
  if groq_response:
114
  return groq_response
115
-
116
- # Fallback responses
117
  message_lower = message.lower()
118
-
119
- if any(word in message_lower for word in ['course', 'courses', 'learn', 'study']):
120
- return """Atomcamp Courses:
121
-
122
- Core Programs:
123
- • Python for Data Science - Master Python programming fundamentals
124
- • Machine Learning Fundamentals - Learn ML algorithms and applications
125
- • Deep Learning with TensorFlow - Build neural networks and AI models
126
- • Data Visualization - Create stunning charts with Matplotlib & Seaborn
127
- • SQL for Data Analysis - Database querying and data manipulation
128
- • Statistics for Data Science - Statistical analysis and hypothesis testing
129
-
130
- Learning Tracks:
131
- • Beginner Track (3 months) - Perfect for newcomers
132
- • Intermediate Track (6 months) - Build real-world projects
133
- • Advanced Track (9 months) - Industry-ready with job placement
134
-
135
- Would you like details about any specific course?"""
136
-
137
- elif any(word in message_lower for word in ['career', 'job', 'placement']):
138
- return """Career Services at Atomcamp:
139
-
140
- Job Placement Support:
141
- • Resume building and optimization
142
- • Technical interview preparation
143
- • Portfolio development guidance
144
- • Direct connections with hiring partners
145
- • Mock interviews with industry experts
146
-
147
- Career Growth:
148
- • Average salary increase: 150-300%
149
- • 95% job placement rate within 6 months
150
- • Access to exclusive job opportunities
151
- • Ongoing career mentorship
152
- • Industry networking events
153
-
154
- Ready to transform your career in data science?"""
155
-
156
- else:
157
- return f"""Thank you for your question about "{message}"!
158
 
159
- As an Atomcamp AI assistant, I'm here to help you with:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- Course Information - Learn about our data science programs
162
- Career Guidance - Job placement and career growth
163
- Technical Topics - Python, ML, AI, and data analysis
164
- Getting Started - How to begin your data science journey
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
- Would you like me to elaborate on any specific aspect?"""
 
 
 
 
 
 
 
 
 
167
 
168
  @app.route('/')
169
  def index():
170
- html_template = """
171
- <!DOCTYPE html>
172
- <html lang="en">
173
- <head>
174
- <meta charset="UTF-8">
175
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
176
- <title>atomcamp AI Chatbot</title>
177
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
178
- <style>
179
- * {
180
- margin: 0;
181
- padding: 0;
182
- box-sizing: border-box;
183
- }
184
-
185
- body {
186
- font-family: 'Inter', sans-serif;
187
- background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
188
- min-height: 100vh;
189
- display: flex;
190
- flex-direction: column;
191
- }
192
-
193
- .header {
194
- display: flex;
195
- flex-direction: column;
196
- align-items: center;
197
- padding-top: 2rem;
198
- padding-bottom: 1.5rem;
199
- }
200
-
201
- .logo {
202
- margin-bottom: 1.5rem;
203
- }
204
-
205
- .logo-placeholder {
206
- width: 160px;
207
- height: 48px;
208
- background: #22c55e;
209
- border-radius: 24px;
210
- display: flex;
211
- align-items: center;
212
- justify-content: center;
213
- color: white;
214
- font-weight: 700;
215
- font-size: 18px;
216
- }
217
-
218
- .title-bubble {
219
- width: 100%;
220
- max-width: 100%;
221
- padding: 0 1rem;
222
- }
223
-
224
- .bubble-container {
225
- background: #f0fdf4;
226
- border: 1px solid #e5e7eb;
227
- border-radius: 1rem;
228
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
229
- padding: 1.5rem;
230
- max-width: fit-content;
231
- }
232
-
233
- .bubble-content {
234
- text-align: left;
235
- }
236
-
237
- .chatbot-title {
238
- font-size: 1.5rem;
239
- font-weight: 600;
240
- color: #166534;
241
- margin-bottom: 0.25rem;
242
- letter-spacing: -0.025em;
243
- }
244
-
245
- .typing-indicator {
246
- display: flex;
247
- align-items: center;
248
- gap: 0.5rem;
249
- }
250
-
251
- .typing-dots {
252
- display: flex;
253
- gap: 0.25rem;
254
- }
255
-
256
- .typing-dot {
257
- width: 0.375rem;
258
- height: 0.375rem;
259
- background: #166534;
260
- border-radius: 50%;
261
- animation: bounce 1s infinite;
262
- }
263
-
264
- .typing-dot:nth-child(2) {
265
- animation-delay: 0.1s;
266
- }
267
-
268
- .typing-dot:nth-child(3) {
269
- animation-delay: 0.2s;
270
- }
271
-
272
- .typing-text {
273
- color: #166534;
274
- font-size: 1rem;
275
- font-weight: 500;
276
- }
277
-
278
- .status-indicator {
279
- display: flex;
280
- align-items: center;
281
- gap: 0.5rem;
282
- }
283
-
284
- .status-dot {
285
- width: 0.5rem;
286
- height: 0.5rem;
287
- border-radius: 50%;
288
- background: #16a34a;
289
- }
290
-
291
- .status-text {
292
- font-size: 0.875rem;
293
- font-weight: 500;
294
- color: #166534;
295
- }
296
-
297
- .chat-container {
298
- width: 100%;
299
- margin: 0;
300
- padding: 0 1rem 8rem 1rem;
301
- flex: 1;
302
- overflow-y: auto;
303
- height: calc(100vh - 300px);
304
-
305
- }
306
-
307
- .welcome-screen {
308
- text-align: center;
309
- padding: 3rem 0;
310
- }
311
-
312
- .welcome-card {
313
- background: white;
314
- border-radius: 1rem;
315
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
316
- padding: 2rem;
317
- max-width: 36rem;
318
- margin: 0 auto;
319
- }
320
-
321
- .welcome-icon {
322
- width: 3rem;
323
- height: 3rem;
324
- background: #22c55e;
325
- border-radius: 0.75rem;
326
- display: flex;
327
- align-items: center;
328
- justify-content: center;
329
- margin: 0 auto 1rem;
330
- color: white;
331
- font-size: 1.5rem;
332
- font-weight: bold;
333
- }
334
-
335
- .welcome-title {
336
- font-size: 1.25rem;
337
- font-weight: 600;
338
- color: #1f2937;
339
- margin-bottom: 0.75rem;
340
- letter-spacing: -0.025em;
341
- }
342
-
343
- .welcome-description {
344
- color: #6b7280;
345
- font-size: 0.875rem;
346
- line-height: 1.5;
347
- margin-bottom: 1.5rem;
348
- }
349
-
350
- .feature-grid {
351
- display: grid;
352
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
353
- gap: 0.75rem;
354
- text-align: left;
355
- }
356
-
357
- .feature-card {
358
- background: #f9fafb;
359
- border-radius: 0.5rem;
360
- padding: 0.75rem;
361
- }
362
-
363
- .feature-title {
364
- font-weight: 600;
365
- color: #1f2937;
366
- margin-bottom: 0.5rem;
367
- font-size: 0.875rem;
368
- }
369
-
370
- .feature-list {
371
- font-size: 0.75rem;
372
- color: #6b7280;
373
- line-height: 1.5;
374
- list-style: none;
375
- padding: 0;
376
- margin: 0;
377
- }
378
-
379
- .feature-list li {
380
- margin-bottom: 0.25rem;
381
- }
382
-
383
- .messages-container {
384
- display: flex;
385
- flex-direction: column;
386
- gap: 1rem;
387
- }
388
-
389
- .message {
390
- display: flex;
391
- animation: fadeIn 0.3s ease-out;
392
- }
393
-
394
- .message.user {
395
- justify-content: flex-end;
396
- }
397
-
398
- .message.assistant {
399
- justify-content: flex-start;
400
- }
401
-
402
- .message-content {
403
- display: flex;
404
- align-items: flex-end;
405
- gap: 0.5rem;
406
- max-width: 48rem;
407
- }
408
-
409
- .message.user .message-content {
410
- max-width: 28rem;
411
- }
412
-
413
- .avatar {
414
- width: 2rem;
415
- height: 2rem;
416
- border-radius: 50%;
417
- display: flex;
418
- align-items: center;
419
- justify-content: center;
420
- flex-shrink: 0;
421
- }
422
-
423
- .avatar.user {
424
- background: #22c55e;
425
- color: white;
426
- }
427
-
428
- .avatar.assistant {
429
- background: #e5e7eb;
430
- color: #6b7280;
431
- }
432
-
433
- .message-bubble {
434
- padding: 0.75rem 1rem;
435
- border-radius: 1rem;
436
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
437
- }
438
-
439
- .message.user .message-bubble {
440
- background: #22c55e;
441
- color: white;
442
- border-bottom-right-radius: 0.375rem;
443
- }
444
-
445
- .message.assistant .message-bubble {
446
- background: #e5e7eb;
447
- color: #1f2937;
448
- border-bottom-left-radius: 0.375rem;
449
- }
450
-
451
- .message-text {
452
- font-size: 0.875rem;
453
- font-weight: 500;
454
- line-height: 1.5;
455
- }
456
-
457
- .message-formatted {
458
- font-size: 0.875rem;
459
- line-height: 1.5;
460
- }
461
-
462
- .input-area {
463
- position: fixed;
464
- bottom: 0;
465
- left: 0;
466
- right: 0;
467
- background: rgba(255, 255, 255, 0.95);
468
- backdrop-filter: blur(8px);
469
- border-top: 1px solid #e5e7eb;
470
- padding: 1rem;
471
- }
472
-
473
- .input-container {
474
- max-width: 100%;
475
- margin: 0 auto;
476
- }
477
-
478
- .input-form {
479
- position: relative;
480
- }
481
-
482
- .input-wrapper {
483
- position: relative;
484
- background: #4b5563;
485
- border-radius: 9999px;
486
- overflow: hidden;
487
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
488
- }
489
-
490
- .message-input {
491
- width: 100%;
492
- padding: 0.875rem 1.25rem;
493
- padding-right: 3rem;
494
- background: transparent;
495
- border: none;
496
- outline: none;
497
- color: white;
498
- font-size: 0.875rem;
499
- font-weight: 500;
500
- letter-spacing: 0.025em;
501
- }
502
-
503
- .message-input::placeholder {
504
- color: #d1d5db;
505
- }
506
-
507
- .send-button {
508
- position: absolute;
509
- right: 0.5rem;
510
- top: 50%;
511
- transform: translateY(-50%);
512
- background: #22c55e;
513
- color: white;
514
- border: none;
515
- border-radius: 50%;
516
- width: 2.5rem;
517
- height: 2.5rem;
518
- display: flex;
519
- align-items: center;
520
- justify-content: center;
521
- cursor: pointer;
522
- transition: all 0.2s;
523
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
524
- }
525
-
526
- .send-button:hover:not(:disabled) {
527
- background: #16a34a;
528
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
529
- }
530
-
531
- .send-button:disabled {
532
- opacity: 0.5;
533
- cursor: not-allowed;
534
- }
535
-
536
- .status-bar {
537
- display: flex;
538
- justify-content: space-between;
539
- align-items: center;
540
- margin-top: 0.5rem;
541
- padding: 0 0.5rem;
542
- }
543
-
544
- .connection-status {
545
- font-size: 0.75rem;
546
- font-weight: 500;
547
- color: #6b7280;
548
- }
549
-
550
- .char-count {
551
- font-size: 0.75rem;
552
- font-weight: 500;
553
- color: #9ca3af;
554
- }
555
-
556
- .hidden {
557
- display: none !important;
558
- }
559
-
560
- @keyframes bounce {
561
- 0%, 80%, 100% {
562
- transform: scale(0);
563
- }
564
- 40% {
565
- transform: scale(1);
566
- }
567
- }
568
-
569
- @keyframes fadeIn {
570
- from {
571
- opacity: 0;
572
- transform: translateY(8px);
573
- }
574
- to {
575
- opacity: 1;
576
- transform: translateY(0);
577
- }
578
- }
579
-
580
- @media (max-width: 768px) {
581
- .header {
582
- padding-top: 1rem;
583
- padding-bottom: 1rem;
584
- }
585
-
586
- .chat-container {
587
- padding-bottom: 6rem;
588
- }
589
-
590
- .message-content {
591
- max-width: calc(100vw - 2rem);
592
- }
593
-
594
- .message.user .message-content {
595
- max-width: calc(100vw - 2rem);
596
- }
597
- }
598
- </style>
599
- </head>
600
- <body>
601
- <div class="header">
602
- <div class="logo">
603
- <img src="/static/atomcamp_logo.png" alt="Atomcamp Logo" style="height: 48px;">
604
- </div>
605
-
606
- <div class="title-bubble">
607
- <div class="bubble-container">
608
- <div class="bubble-content">
609
- <h1 class="chatbot-title">Chatbot</h1>
610
-
611
- <div id="typingIndicator" class="typing-indicator hidden">
612
- <div class="typing-dots">
613
- <div class="typing-dot"></div>
614
- <div class="typing-dot"></div>
615
- <div class="typing-dot"></div>
616
- </div>
617
- <span class="typing-text">Typing...</span>
618
- </div>
619
-
620
- <div id="statusIndicator" class="status-indicator">
621
- <div class="status-dot"></div>
622
- <span class="status-text">Online</span>
623
- </div>
624
- </div>
625
- </div>
626
- </div>
627
- </div>
628
-
629
- <div class="chat-container">
630
- <div id="welcomeScreen" class="welcome-screen">
631
- <div class="welcome-card">
632
- <div class="welcome-icon">✨</div>
633
- <h2 class="welcome-title">Welcome to atomcamp AI</h2>
634
- <p class="welcome-description">Ask me about courses, data science concepts, and learning paths.</p>
635
-
636
- <div class="feature-grid">
637
- <div class="feature-card">
638
- <h3 class="feature-title">Ask me about:</h3>
639
- <ul class="feature-list">
640
- <li>• Course information</li>
641
- <li>• Data science concepts</li>
642
- <li>• Learning paths</li>
643
- </ul>
644
- </div>
645
- <div class="feature-card">
646
- <h3 class="feature-title">Try asking:</h3>
647
- <ul class="feature-list">
648
- <li>• "What courses do you offer?"</li>
649
- <li>• "Explain machine learning"</li>
650
- <li>• "How do I get started?"</li>
651
- </ul>
652
- </div>
653
- </div>
654
- </div>
655
- </div>
656
-
657
- <div id="messagesContainer" class="messages-container hidden"></div>
658
- </div>
659
-
660
- <div class="input-area">
661
- <div class="input-container">
662
- <form id="chatForm" class="input-form">
663
- <div class="input-wrapper">
664
- <input
665
- type="text"
666
- id="messageInput"
667
- class="message-input"
668
- placeholder="Ask me anything about atomcamp..."
669
- maxlength="1000"
670
- autocomplete="off"
671
- >
672
- <button type="submit" id="sendButton" class="send-button">
673
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
674
- <path d="M12 19V5M5 12l7-7 7 7"/>
675
- </svg>
676
- </button>
677
- </div>
678
-
679
- <div class="status-bar">
680
- <span id="connectionStatus" class="connection-status">Connected to atomcamp AI</span>
681
- <span id="charCount" class="char-count">0/1000</span>
682
- </div>
683
- </form>
684
- </div>
685
- </div>
686
-
687
- <script>
688
- let messages = [];
689
- let isTyping = false;
690
- let isConnected = true;
691
-
692
- const welcomeScreen = document.getElementById('welcomeScreen');
693
- const messagesContainer = document.getElementById('messagesContainer');
694
- const chatForm = document.getElementById('chatForm');
695
- const messageInput = document.getElementById('messageInput');
696
- const sendButton = document.getElementById('sendButton');
697
- const typingIndicator = document.getElementById('typingIndicator');
698
- const statusIndicator = document.getElementById('statusIndicator');
699
- const connectionStatus = document.getElementById('connectionStatus');
700
- const charCount = document.getElementById('charCount');
701
-
702
- chatForm.addEventListener('submit', handleSubmit);
703
- messageInput.addEventListener('input', updateCharCount);
704
-
705
- function updateCharCount() {
706
- const count = messageInput.value.length;
707
- charCount.textContent = `${count}/1000`;
708
- }
709
-
710
- async function handleSubmit(e) {
711
- e.preventDefault();
712
- const message = messageInput.value.trim();
713
-
714
- if (!message || isTyping) return;
715
-
716
- addMessage(message, 'user');
717
- messageInput.value = '';
718
- updateCharCount();
719
- setTyping(true);
720
-
721
- try {
722
- const response = await fetch('/chat', {
723
- method: 'POST',
724
- headers: { 'Content-Type': 'application/json' },
725
- body: JSON.stringify({ message: message })
726
- });
727
-
728
- const data = await response.json();
729
-
730
- await new Promise(resolve => setTimeout(resolve, 1200));
731
-
732
- addMessage(data.response || 'Sorry, I could not generate a response.', 'assistant');
733
- setConnected(true);
734
-
735
- } catch (error) {
736
- console.error('Error:', error);
737
- setConnected(false);
738
- addMessage('I am having trouble connecting. Please try again.', 'assistant');
739
- } finally {
740
- setTyping(false);
741
- }
742
- }
743
-
744
- function addMessage(content, role) {
745
- if (messages.length === 0) {
746
- welcomeScreen.classList.add('hidden');
747
- messagesContainer.classList.remove('hidden');
748
- }
749
-
750
- const messageDiv = document.createElement('div');
751
- messageDiv.className = `message ${role}`;
752
-
753
- const messageContent = document.createElement('div');
754
- messageContent.className = 'message-content';
755
-
756
- const avatar = document.createElement('div');
757
- avatar.className = `avatar ${role}`;
758
-
759
- if (role === 'user') {
760
- 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>';
761
- } else {
762
- 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>';
763
- }
764
-
765
- const bubble = document.createElement('div');
766
- bubble.className = 'message-bubble';
767
-
768
- if (role === 'user') {
769
- bubble.innerHTML = `<div class="message-text">${content}</div>`;
770
- messageContent.appendChild(bubble);
771
- messageContent.appendChild(avatar);
772
- } else {
773
- bubble.innerHTML = `<div class="message-formatted">${formatMessageContent(content)}</div>`;
774
- messageContent.appendChild(avatar);
775
- messageContent.appendChild(bubble);
776
- }
777
-
778
- messageDiv.appendChild(messageContent);
779
- messagesContainer.appendChild(messageDiv);
780
-
781
- scrollToBottom();
782
- messages.push({ content, role, timestamp: new Date() });
783
- }
784
-
785
- function formatMessageContent(content) {
786
- return content.split('\\n').map(line => {
787
- if (line.trim().startsWith('•') || line.trim().startsWith('-')) {
788
- return `<div style="display: flex; align-items: flex-start; margin-bottom: 0.375rem;">
789
- <span style="color: #16a34a; margin-right: 0.5rem; margin-top: 0.125rem; font-size: 0.875rem; font-weight: 500;">•</span>
790
- <span style="font-size: 0.875rem; line-height: 1.5;">${line.replace(/^[•-]\\s*/, '')}</span>
791
- </div>`;
792
- } else if (/^\\d+\\./.test(line.trim())) {
793
- const match = line.match(/^\\d+\\./);
794
- return `<div style="display: flex; align-items: flex-start; margin-bottom: 0.375rem;">
795
- <span style="color: #16a34a; margin-right: 0.5rem; font-weight: 600; font-size: 0.875rem;">${match ? match[0] : ''}</span>
796
- <span style="font-size: 0.875rem; line-height: 1.5;">${line.replace(/^\\d+\\.\\s*/, '')}</span>
797
- </div>`;
798
- } else if (line.trim() === '') {
799
- return '<br>';
800
- } else {
801
- return `<p style="margin-bottom: 0.375rem; font-size: 0.875rem; line-height: 1.5;">${line}</p>`;
802
- }
803
- }).join('');
804
- }
805
-
806
- function setTyping(typing) {
807
- isTyping = typing;
808
- sendButton.disabled = typing;
809
-
810
- if (typing) {
811
- typingIndicator.classList.remove('hidden');
812
- statusIndicator.classList.add('hidden');
813
- } else {
814
- typingIndicator.classList.add('hidden');
815
- statusIndicator.classList.remove('hidden');
816
- }
817
- }
818
-
819
- function setConnected(connected) {
820
- isConnected = connected;
821
- connectionStatus.textContent = connected ? 'Connected to atomcamp AI' : 'Connection lost';
822
- }
823
-
824
- function scrollToBottom() {
825
- setTimeout(() => {
826
- window.scrollTo({
827
- top: document.body.scrollHeight,
828
- behavior: 'smooth'
829
- });
830
- }, 100);
831
- }
832
- </script>
833
- </body>
834
- </html>
835
- """
836
- return render_template_string(html_template)
837
 
838
  @app.route('/chat', methods=['POST'])
839
  def chat():
840
  try:
841
  data = request.get_json()
842
  message = data.get('message', '')
843
-
844
  if not message:
845
  return jsonify({'error': 'No message provided'}), 400
846
-
847
  if retriever:
848
  docs = retriever.get_relevant_documents(message)
849
  context = "\n\n".join([doc.page_content for doc in docs[:3]])
850
  else:
851
  context = "I'm an AI assistant for Atomcamp, a data science education platform."
852
-
853
  response = generate_response(message, context)
854
  return jsonify({'response': response})
855
-
856
  except Exception as e:
857
  return jsonify({'error': f'Error: {str(e)}'}), 500
858
 
 
24
 
25
  def initialize_vectorstore():
26
  global vectorstore, retriever
27
+
28
  try:
29
  vectorstore = FAISS.load_local("atomcamp_vector_db", embeddings, allow_dangerous_deserialization=True)
30
  retriever = vectorstore.as_retriever()
 
44
  "url": "https://www.atomcamp.com/learning-paths"
45
  }
46
  ]
47
+
48
  docs = [Document(page_content=item["text"], metadata={"url": item["url"]}) for item in sample_data]
49
  splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
50
  chunks = splitter.split_documents(docs)
51
+
52
  vectorstore = FAISS.from_documents(chunks, embeddings)
53
  vectorstore.save_local("atomcamp_vector_db")
54
  retriever = vectorstore.as_retriever()
55
+
56
  return "Sample vectorstore created successfully"
57
 
58
  def call_groq_api(message, context):
59
  global groq_api_key
60
+
61
  if not groq_api_key:
62
  return None
63
+
64
  try:
65
  system_prompt = f"""You are an AI assistant for Atomcamp, a data science education platform.
66
  Use the following context to answer questions about Atomcamp's courses, career services, and data science topics.
67
+
68
  Context: {context}
69
+
70
  Guidelines:
71
  - Be helpful and informative
72
  - Focus on Atomcamp's offerings
 
75
  - Keep responses concise but comprehensive
76
  - Do not use emojis
77
  """
78
+
79
  headers = {
80
  "Authorization": f"Bearer {groq_api_key}",
81
  "Content-Type": "application/json"
82
  }
83
+
84
  data = {
85
  "messages": [
86
  {"role": "system", "content": system_prompt},
 
90
  "temperature": 0.7,
91
  "max_tokens": 1000
92
  }
93
+
94
  response = requests.post(
95
  "https://api.groq.com/openai/v1/chat/completions",
96
  headers=headers,
97
  json=data,
98
  timeout=30
99
  )
100
+
101
  if response.status_code == 200:
102
  result = response.json()
103
  return result["choices"][0]["message"]["content"]
104
  else:
105
  return None
106
+
107
  except Exception as e:
108
  return None
109
 
110
  def generate_response(message, context):
111
  groq_response = call_groq_api(message, context)
112
+
113
  if groq_response:
114
  return groq_response
115
+
 
116
  message_lower = message.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ if any(word in message_lower for word in ['course', 'courses', 'learn', 'study']):
119
+ return """<strong>Atomcamp Courses:</strong><br><br>
120
+ <ul>
121
+ <li><strong>Python for Data Science</strong> - Master Python programming fundamentals</li>
122
+ <li><strong>Machine Learning Fundamentals</strong> - Learn ML algorithms and applications</li>
123
+ <li><strong>Deep Learning with TensorFlow</strong> - Build neural networks and AI models</li>
124
+ <li><strong>Data Visualization</strong> - Create stunning charts with Matplotlib & Seaborn</li>
125
+ <li><strong>SQL for Data Analysis</strong> - Database querying and data manipulation</li>
126
+ <li><strong>Statistics for Data Science</strong> - Statistical analysis and hypothesis testing</li>
127
+ </ul><br>
128
+ <strong>Learning Tracks:</strong><br>
129
+ <ul>
130
+ <li>Beginner Track (3 months) - Perfect for newcomers</li>
131
+ <li>Intermediate Track (6 months) - Build real-world projects</li>
132
+ <li>Advanced Track (9 months) - Industry-ready with job placement</li>
133
+ </ul><br>
134
+ Would you like details about any specific course?"""
135
 
136
+ elif any(word in message_lower for word in ['career', 'job', 'placement']):
137
+ return """<strong>Career Services at Atomcamp:</strong><br><br>
138
+ <ul>
139
+ <li>Resume building and optimization</li>
140
+ <li>Technical interview preparation</li>
141
+ <li>Portfolio development guidance</li>
142
+ <li>Direct connections with hiring partners</li>
143
+ <li>Mock interviews with industry experts</li>
144
+ </ul><br>
145
+ <strong>Career Growth:</strong><br>
146
+ <ul>
147
+ <li>Average salary increase: 150-300%</li>
148
+ <li>95% job placement rate within 6 months</li>
149
+ <li>Access to exclusive job opportunities</li>
150
+ <li>Ongoing career mentorship</li>
151
+ <li>Industry networking events</li>
152
+ </ul><br>
153
+ Ready to transform your career in data science?"""
154
 
155
+ else:
156
+ return f"""Thank you for your question about <strong>{message}</strong>!<br><br>
157
+ As an Atomcamp AI assistant, I'm here to help you with:<br>
158
+ <ul>
159
+ <li>Course Information - Learn about our data science programs</li>
160
+ <li>Career Guidance - Job placement and career growth</li>
161
+ <li>Technical Topics - Python, ML, AI, and data analysis</li>
162
+ <li>Getting Started - How to begin your data science journey</li>
163
+ </ul><br>
164
+ Would you like me to elaborate on any specific aspect?"""
165
 
166
  @app.route('/')
167
  def index():
168
+ return render_template_string("""<h2>Atomcamp Chatbot is running. Please deploy with UI.</h2>""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
  @app.route('/chat', methods=['POST'])
171
  def chat():
172
  try:
173
  data = request.get_json()
174
  message = data.get('message', '')
175
+
176
  if not message:
177
  return jsonify({'error': 'No message provided'}), 400
178
+
179
  if retriever:
180
  docs = retriever.get_relevant_documents(message)
181
  context = "\n\n".join([doc.page_content for doc in docs[:3]])
182
  else:
183
  context = "I'm an AI assistant for Atomcamp, a data science education platform."
184
+
185
  response = generate_response(message, context)
186
  return jsonify({'response': response})
187
+
188
  except Exception as e:
189
  return jsonify({'error': f'Error: {str(e)}'}), 500
190