AptlyDigital commited on
Commit
5d5c300
·
verified ·
1 Parent(s): 3024f65

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +511 -45
index.html CHANGED
@@ -8,7 +8,7 @@
8
  <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
  <style>
11
- /* ALL EXISTING CSS REMAINS EXACTLY THE SAME */
12
  * {
13
  margin: 0;
14
  padding: 0;
@@ -346,12 +346,21 @@
346
  transform: translateY(-2px);
347
  }
348
 
 
349
  .card {
350
  background: rgba(20, 20, 30, 0.8);
351
  border-radius: 15px;
352
  padding: 20px;
353
  border: 1px solid rgba(255, 255, 255, 0.1);
354
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
 
 
 
 
 
 
 
 
355
  }
356
 
357
  .card-title {
@@ -562,6 +571,53 @@
562
  box-shadow: 0 0 0 2px rgba(90, 108, 255, 0.2);
563
  }
564
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  .rag-controls {
566
  display: grid;
567
  grid-template-columns: 1fr 1fr;
@@ -759,6 +815,17 @@
759
  .chat-messages {
760
  height: 250px;
761
  }
 
 
 
 
 
 
 
 
 
 
 
762
  }
763
 
764
  @media (max-width: 768px) {
@@ -774,6 +841,14 @@
774
  width: 100%;
775
  max-width: 350px;
776
  }
 
 
 
 
 
 
 
 
777
  }
778
 
779
  @media (max-width: 480px) {
@@ -798,6 +873,10 @@
798
  min-height: 48px;
799
  height: 48px;
800
  }
 
 
 
 
801
  }
802
  </style>
803
  </head>
@@ -892,6 +971,21 @@
892
  <div class="accordion-content" id="trainingControls">
893
  <textarea class="rag-textarea" id="knowledgeText" placeholder="Add knowledge for AI training..."></textarea>
894
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
  <div class="rag-controls">
896
  <button class="rag-btn" id="addKnowledgeBtn">
897
  <i class="fas fa-plus"></i>
@@ -1077,14 +1171,15 @@
1077
  <input type="text" class="rag-textarea" id="modelName" placeholder="microsoft/DialoGPT-medium">
1078
  </div>
1079
 
 
1080
  <div class="rag-controls">
1081
  <button class="rag-btn" id="testApiBtn">
1082
  <i class="fas fa-wifi"></i>
1083
  Test Connection
1084
  </button>
1085
- <button class="rag-btn primary" id="saveApiBtn">
1086
- <i class="fas fa-save"></i>
1087
- Save Settings
1088
  </button>
1089
  </div>
1090
 
@@ -1131,7 +1226,7 @@
1131
  document.getElementById('loadingText').textContent = text;
1132
  }
1133
 
1134
- // ==================== FULLY FUNCTIONAL LLM INTEGRATION ====================
1135
  class LLMIntegration {
1136
  constructor() {
1137
  this.provider = 'huggingface';
@@ -1140,6 +1235,7 @@
1140
  this.modelName = '';
1141
  this.isConnected = false;
1142
  this.connectionTested = false;
 
1143
 
1144
  // Default endpoints for each provider
1145
  this.defaultEndpoints = {
@@ -1235,10 +1331,20 @@
1235
  this.testConnection();
1236
  });
1237
 
1238
- // Save settings button
1239
- document.getElementById('saveApiBtn').addEventListener('click', () => {
1240
- this.saveSettings();
1241
- this.showApiStatus('Settings saved successfully!', 'success');
 
 
 
 
 
 
 
 
 
 
1242
  });
1243
  }
1244
 
@@ -1282,6 +1388,56 @@
1282
  }
1283
  }
1284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1285
  async testConnection() {
1286
  if (!this.apiEndpoint) {
1287
  this.showApiStatus('Please enter an API endpoint', 'error');
@@ -1850,18 +2006,20 @@
1850
  }
1851
  }
1852
 
1853
- // RAG AI Tutor System
1854
  class RAGAITutor {
1855
  constructor() {
1856
  this.knowledgeBase = [];
1857
  this.vectorStore = null;
1858
  this.isTraining = false;
1859
  this.currentSession = null;
 
1860
  this.init();
1861
  }
1862
 
1863
  init() {
1864
  this.setupEventListeners();
 
1865
  this.loadSampleKnowledge();
1866
  }
1867
 
@@ -1891,6 +2049,144 @@
1891
  document.getElementById('trainBtn').addEventListener('click', () => {
1892
  this.trainRAGModel();
1893
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1894
  }
1895
 
1896
  addKnowledgeFromText() {
@@ -1915,10 +2211,19 @@
1915
  this.addKnowledgeItem(knowledgeItem);
1916
  textArea.value = '';
1917
  this.showRAGStatus('Knowledge added successfully!', 'success');
 
 
 
 
 
 
 
 
1918
  }
1919
 
1920
  addKnowledgeItem(item) {
1921
  this.knowledgeBase.push(item);
 
1922
  }
1923
 
1924
  async trainRAGModel() {
@@ -1933,23 +2238,49 @@
1933
  const progressBar = document.getElementById('trainingProgress');
1934
  progressBar.style.width = '0%';
1935
 
1936
- const steps = ['Preprocessing text', 'Creating embeddings', 'Building vector store', 'Optimizing retrieval'];
 
 
 
 
 
 
 
 
 
 
 
 
1937
 
1938
  for (let i = 0; i < steps.length; i++) {
1939
  this.showRAGStatus(steps[i], 'processing');
1940
  progressBar.style.width = `${((i + 1) / steps.length) * 100}%`;
1941
 
1942
- await this.sleep(1000);
1943
  }
1944
 
1945
  this.vectorStore = {
1946
  size: this.knowledgeBase.length,
1947
  trainedAt: new Date().toISOString(),
1948
- model: 'all-MiniLM-L6-v2'
 
 
1949
  };
1950
 
1951
  this.isTraining = false;
1952
- this.showRAGStatus(`RAG training complete! ${this.knowledgeBase.length} documents indexed.`, 'success');
 
 
 
 
 
 
 
 
 
 
 
 
1953
 
1954
  setTimeout(() => {
1955
  progressBar.style.width = '0%';
@@ -1973,22 +2304,49 @@
1973
  findRelevantKnowledge(query) {
1974
  const queryLower = query.toLowerCase();
1975
  const relevant = [];
 
1976
 
 
1977
  for (const item of this.knowledgeBase) {
1978
- if (item.content.toLowerCase().includes(queryLower) ||
1979
- item.title.toLowerCase().includes(queryLower)) {
1980
- relevant.push(item);
1981
-
1982
- if (relevant.length >= 3) break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1983
  }
1984
  }
1985
 
1986
- return relevant;
 
 
 
 
1987
  }
1988
 
1989
  generateResponse(query, relevantKnowledge) {
1990
- const context = relevantKnowledge.map(item => item.content).join('\n\n');
1991
- return `Based on my knowledge base:\n\n${context}\n\nRegarding your question "${query}", here's what I can explain...`;
 
 
 
1992
  }
1993
 
1994
  showRAGStatus(message, type = 'info') {
@@ -2448,7 +2806,7 @@
2448
  }
2449
  }
2450
 
2451
- // 3D Visualization
2452
  class AITutorVisualization {
2453
  constructor() {
2454
  if (!checkThreeJS()) return;
@@ -2503,7 +2861,7 @@
2503
  antialias: true,
2504
  alpha: true
2505
  });
2506
- this.renderer.setSize(window.innerWidth, window.innerHeight);
2507
  this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
2508
 
2509
  this.scene.fog = new THREE.Fog(0x0a0a0f, 10, 25);
@@ -2515,9 +2873,20 @@
2515
  directionalLight.position.set(5, 3, 5);
2516
  this.scene.add(directionalLight);
2517
 
 
 
 
 
 
 
 
 
 
2518
  this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
2519
  this.controls.enableDamping = true;
2520
  this.controls.dampingFactor = 0.05;
 
 
2521
  }
2522
 
2523
  createParticles() {
@@ -2525,22 +2894,33 @@
2525
  const positions = new Float32Array(this.params.particleCount * 3);
2526
  const colors = new Float32Array(this.params.particleCount * 3);
2527
 
 
2528
  for (let i = 0; i < this.params.particleCount; i++) {
2529
  const i3 = i * 3;
 
 
 
2530
  const phi = Math.acos(-1 + (2 * i) / this.params.particleCount);
2531
  const theta = Math.sqrt(this.params.particleCount * Math.PI) * phi;
2532
 
2533
- const x = Math.cos(theta) * Math.sin(phi);
2534
- const y = Math.sin(theta) * Math.sin(phi);
2535
- const z = Math.cos(phi);
 
 
2536
 
2537
  positions[i3] = x;
2538
  positions[i3 + 1] = y;
2539
  positions[i3 + 2] = z;
2540
 
2541
- colors[i3] = 0.5 + x * 0.5;
2542
- colors[i3 + 1] = 0.3 + y * 0.7;
2543
- colors[i3 + 2] = 0.8 + z * 0.2;
 
 
 
 
 
2544
  }
2545
 
2546
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
@@ -2551,21 +2931,23 @@
2551
  vertexColors: true,
2552
  transparent: true,
2553
  opacity: 0.8,
2554
- blending: THREE.AdditiveBlending
 
2555
  });
2556
 
2557
  this.particles = new THREE.Points(geometry, material);
2558
  this.scene.add(this.particles);
2559
 
2560
- const testGeometry = new THREE.SphereGeometry(0.5, 32, 32);
2561
- const testMaterial = new THREE.MeshBasicMaterial({
 
2562
  color: 0x5a6cff,
2563
  wireframe: true,
2564
  transparent: true,
2565
- opacity: 0.1
2566
  });
2567
- const testSphere = new THREE.Mesh(testGeometry, testMaterial);
2568
- this.scene.add(testSphere);
2569
  }
2570
 
2571
  setupControls() {
@@ -2577,6 +2959,11 @@
2577
  const particleCountValue = document.getElementById('particleCountValue');
2578
  const energyValue = document.getElementById('energyValue');
2579
 
 
 
 
 
 
2580
  intensitySlider.addEventListener('input', (e) => {
2581
  this.params.intensity = e.target.value / 100;
2582
  intensityValue.textContent = e.target.value;
@@ -2593,6 +2980,7 @@
2593
  energyValue.textContent = e.target.value;
2594
  });
2595
 
 
2596
  document.querySelectorAll('.preset-btn').forEach(btn => {
2597
  btn.addEventListener('click', (e) => {
2598
  const preset = e.target.dataset.preset;
@@ -2603,16 +2991,53 @@
2603
  document.getElementById('retryBtn').addEventListener('click', () => {
2604
  location.reload();
2605
  });
 
 
 
2606
  }
2607
 
2608
  setPreset(preset) {
2609
  this.params.mode = preset;
2610
 
 
2611
  document.querySelectorAll('.preset-btn').forEach(btn => {
2612
  btn.style.background = btn.dataset.preset === preset
2613
  ? 'rgba(90, 108, 255, 0.4)'
2614
  : 'rgba(90, 108, 255, 0.1)';
 
 
 
2615
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2616
  }
2617
 
2618
  updateParticleCount() {
@@ -2637,6 +3062,7 @@
2637
  if (!this.particles) return;
2638
 
2639
  const positions = this.particles.geometry.attributes.position.array;
 
2640
  const originalPositions = this.particles.geometry.attributes.originalPosition;
2641
 
2642
  if (!originalPositions) {
@@ -2656,41 +3082,55 @@
2656
 
2657
  let radius = 2.0;
2658
  let intensity = this.params.intensity;
 
2659
 
 
2660
  switch(this.params.mode) {
2661
  case 'listening':
2662
  radius += Math.sin(time * 3 + i * 0.01) * 0.5 * intensity;
 
2663
  break;
2664
  case 'processing':
2665
  radius += Math.sin(time * 5 + i * 0.02) * 0.6 * intensity;
 
2666
  break;
2667
  case 'responding':
2668
  radius += (Math.sin(time * 2 + i * 0.005) + 1) * 0.4 * intensity;
 
2669
  break;
2670
  case 'exploring':
2671
  radius += Math.sin(time * 1.5 + i * 0.015) * 0.7 * intensity;
 
2672
  break;
2673
  case 'teaching':
2674
  radius += Math.sin(time * 4 + i * 0.008) * 0.55 * intensity;
 
2675
  break;
2676
- default:
2677
  radius += Math.sin(time * 0.5 + i * 0.01) * 0.2 * intensity;
 
2678
  }
2679
 
2680
- radius += Math.sin(time * 8) * 0.2 * this.params.energy;
2681
-
2682
  const audioIndex = Math.floor((i / this.params.particleCount) * this.audioData.length);
2683
  radius += this.audioData[audioIndex] * 0.5 * intensity;
2684
 
 
2685
  positions[i3] = x * radius;
2686
  positions[i3 + 1] = y * radius;
2687
  positions[i3 + 2] = z * radius;
 
 
 
 
2688
  }
2689
 
2690
  this.particles.geometry.attributes.position.needsUpdate = true;
 
2691
 
2692
- this.particles.rotation.y = time * 0.1;
2693
- this.particles.rotation.x = Math.sin(time * 0.05) * 0.1;
 
2694
  }
2695
 
2696
  animate() {
@@ -2706,9 +3146,13 @@
2706
  }
2707
 
2708
  onWindowResize() {
2709
- this.camera.aspect = window.innerWidth / window.innerHeight;
 
 
 
 
2710
  this.camera.updateProjectionMatrix();
2711
- this.renderer.setSize(window.innerWidth, window.innerHeight);
2712
  }
2713
 
2714
  destroy() {
@@ -2736,10 +3180,10 @@
2736
  // Initialize 3D visualization
2737
  window.visualization = new AITutorVisualization();
2738
 
2739
- // Initialize RAG AI Tutor
2740
  window.ragTutor = new RAGAITutor();
2741
 
2742
- // Initialize FULLY FUNCTIONAL LLM Integration
2743
  window.llmIntegration = new LLMIntegration();
2744
 
2745
  // Initialize chat interface with BOTH RAG and LLM
@@ -2754,6 +3198,7 @@
2754
  if (window.visualization && window.visualization.scene) {
2755
  window.addEventListener('resize', () => window.visualization.onWindowResize());
2756
 
 
2757
  window.addEventListener('keydown', (e) => {
2758
  if (e.code === 'Space') {
2759
  window.visualization.setPreset(
@@ -2763,6 +3208,14 @@
2763
  if (e.code === 'Space' && e.ctrlKey && window.voiceSynthesis) {
2764
  window.voiceSynthesis.stopSpeaking();
2765
  }
 
 
 
 
 
 
 
 
2766
  });
2767
 
2768
  window.visualization.setPreset('idle');
@@ -2774,6 +3227,9 @@
2774
  window.llmIntegration.testConnection().then(connected => {
2775
  if (connected) {
2776
  console.log('LLM API auto-connected on startup');
 
 
 
2777
  }
2778
  });
2779
  }, 2000);
@@ -2781,6 +3237,16 @@
2781
 
2782
  hideLoading();
2783
 
 
 
 
 
 
 
 
 
 
 
2784
  }, 100);
2785
  });
2786
 
 
8
  <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
  <style>
11
+ /* ALL EXISTING CSS REMAINS EXACTLY THE SAME - Only adding fixes */
12
  * {
13
  margin: 0;
14
  padding: 0;
 
346
  transform: translateY(-2px);
347
  }
348
 
349
+ /* CARD FIX - prevent edge cutting */
350
  .card {
351
  background: rgba(20, 20, 30, 0.8);
352
  border-radius: 15px;
353
  padding: 20px;
354
  border: 1px solid rgba(255, 255, 255, 0.1);
355
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
356
+ overflow: visible !important; /* Important fix */
357
+ margin-bottom: 25px;
358
+ position: relative;
359
+ }
360
+
361
+ /* Ensure card contents don't overflow */
362
+ .card > *:not(:last-child) {
363
+ margin-bottom: 15px;
364
  }
365
 
366
  .card-title {
 
571
  box-shadow: 0 0 0 2px rgba(90, 108, 255, 0.2);
572
  }
573
 
574
+ /* NEW: Document upload styles */
575
+ .document-upload-area {
576
+ margin-top: 15px;
577
+ padding: 15px;
578
+ background: rgba(20, 20, 30, 0.5);
579
+ border-radius: 10px;
580
+ border: 2px dashed rgba(90, 108, 255, 0.3);
581
+ text-align: center;
582
+ cursor: pointer;
583
+ transition: all 0.3s ease;
584
+ }
585
+
586
+ .document-upload-area:hover {
587
+ background: rgba(20, 20, 30, 0.7);
588
+ border-color: #5a6cff;
589
+ }
590
+
591
+ .document-upload-area.dragover {
592
+ background: rgba(90, 108, 255, 0.1);
593
+ border-color: #5a6cff;
594
+ }
595
+
596
+ .upload-icon {
597
+ font-size: 2em;
598
+ color: #5a6cff;
599
+ margin-bottom: 10px;
600
+ }
601
+
602
+ .upload-text {
603
+ color: #a0b0ff;
604
+ margin-bottom: 10px;
605
+ }
606
+
607
+ .file-input {
608
+ display: none;
609
+ }
610
+
611
+ .file-info {
612
+ margin-top: 10px;
613
+ padding: 8px;
614
+ background: rgba(90, 108, 255, 0.1);
615
+ border-radius: 8px;
616
+ font-size: 0.85em;
617
+ color: #a0b0ff;
618
+ display: none;
619
+ }
620
+
621
  .rag-controls {
622
  display: grid;
623
  grid-template-columns: 1fr 1fr;
 
815
  .chat-messages {
816
  height: 250px;
817
  }
818
+
819
+ /* Mobile card adjustments */
820
+ .card {
821
+ padding: 15px;
822
+ margin-bottom: 20px;
823
+ }
824
+
825
+ .rag-controls {
826
+ grid-template-columns: 1fr;
827
+ gap: 8px;
828
+ }
829
  }
830
 
831
  @media (max-width: 768px) {
 
841
  width: 100%;
842
  max-width: 350px;
843
  }
844
+
845
+ .document-upload-area {
846
+ padding: 12px;
847
+ }
848
+
849
+ .upload-icon {
850
+ font-size: 1.5em;
851
+ }
852
  }
853
 
854
  @media (max-width: 480px) {
 
873
  min-height: 48px;
874
  height: 48px;
875
  }
876
+
877
+ .button-grid {
878
+ grid-template-columns: repeat(2, 1fr);
879
+ }
880
  }
881
  </style>
882
  </head>
 
971
  <div class="accordion-content" id="trainingControls">
972
  <textarea class="rag-textarea" id="knowledgeText" placeholder="Add knowledge for AI training..."></textarea>
973
 
974
+ <!-- NEW: Document Upload Area -->
975
+ <div class="document-upload-area" id="documentUploadArea">
976
+ <div class="upload-icon">
977
+ <i class="fas fa-cloud-upload-alt"></i>
978
+ </div>
979
+ <div class="upload-text">
980
+ Drag & drop documents or click to upload
981
+ </div>
982
+ <div class="upload-hint">
983
+ Supports: PDF, TXT, DOC, DOCX
984
+ </div>
985
+ <input type="file" id="documentFile" class="file-input" accept=".pdf,.txt,.doc,.docx" multiple>
986
+ </div>
987
+ <div class="file-info" id="fileInfo"></div>
988
+
989
  <div class="rag-controls">
990
  <button class="rag-btn" id="addKnowledgeBtn">
991
  <i class="fas fa-plus"></i>
 
1171
  <input type="text" class="rag-textarea" id="modelName" placeholder="microsoft/DialoGPT-medium">
1172
  </div>
1173
 
1174
+ <!-- NEW: Apply/Submit Button -->
1175
  <div class="rag-controls">
1176
  <button class="rag-btn" id="testApiBtn">
1177
  <i class="fas fa-wifi"></i>
1178
  Test Connection
1179
  </button>
1180
+ <button class="rag-btn primary" id="applyApiBtn">
1181
+ <i class="fas fa-check"></i>
1182
+ Apply Settings
1183
  </button>
1184
  </div>
1185
 
 
1226
  document.getElementById('loadingText').textContent = text;
1227
  }
1228
 
1229
+ // ==================== ENHANCED LLM INTEGRATION ====================
1230
  class LLMIntegration {
1231
  constructor() {
1232
  this.provider = 'huggingface';
 
1235
  this.modelName = '';
1236
  this.isConnected = false;
1237
  this.connectionTested = false;
1238
+ this.isApplying = false;
1239
 
1240
  // Default endpoints for each provider
1241
  this.defaultEndpoints = {
 
1331
  this.testConnection();
1332
  });
1333
 
1334
+ // Apply settings button - NEW
1335
+ document.getElementById('applyApiBtn').addEventListener('click', () => {
1336
+ this.applySettings();
1337
+ });
1338
+
1339
+ // Also apply on Enter key in API fields
1340
+ const apiFields = ['apiEndpoint', 'apiKey', 'modelName'];
1341
+ apiFields.forEach(fieldId => {
1342
+ document.getElementById(fieldId).addEventListener('keydown', (e) => {
1343
+ if (e.key === 'Enter') {
1344
+ e.preventDefault();
1345
+ this.applySettings();
1346
+ }
1347
+ });
1348
  });
1349
  }
1350
 
 
1388
  }
1389
  }
1390
 
1391
+ async applySettings() {
1392
+ if (this.isApplying) return;
1393
+
1394
+ this.isApplying = true;
1395
+ const applyBtn = document.getElementById('applyApiBtn');
1396
+ const originalText = applyBtn.innerHTML;
1397
+
1398
+ try {
1399
+ // Update apply button to show loading
1400
+ applyBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Applying...';
1401
+ applyBtn.disabled = true;
1402
+
1403
+ // Save settings first
1404
+ this.saveSettings();
1405
+
1406
+ // Test connection with new settings
1407
+ const connected = await this.testConnection();
1408
+
1409
+ if (connected) {
1410
+ this.showApiStatus('Settings applied and connected successfully!', 'success');
1411
+
1412
+ // Update chat status
1413
+ if (window.chatInterface && window.chatInterface.chatStatusText) {
1414
+ window.chatInterface.chatStatusText.textContent = 'Connected to ' + this.provider;
1415
+ }
1416
+
1417
+ // Update visualization
1418
+ if (window.visualization) {
1419
+ window.visualization.setPreset('processing');
1420
+ setTimeout(() => {
1421
+ window.visualization.setPreset('idle');
1422
+ }, 1500);
1423
+ }
1424
+ } else {
1425
+ this.showApiStatus('Settings saved but connection failed', 'warning');
1426
+ }
1427
+
1428
+ } catch (error) {
1429
+ console.error('Error applying settings:', error);
1430
+ this.showApiStatus('Error applying settings: ' + error.message, 'error');
1431
+ } finally {
1432
+ // Reset apply button
1433
+ setTimeout(() => {
1434
+ applyBtn.innerHTML = originalText;
1435
+ applyBtn.disabled = false;
1436
+ this.isApplying = false;
1437
+ }, 1000);
1438
+ }
1439
+ }
1440
+
1441
  async testConnection() {
1442
  if (!this.apiEndpoint) {
1443
  this.showApiStatus('Please enter an API endpoint', 'error');
 
2006
  }
2007
  }
2008
 
2009
+ // ENHANCED RAG AI Tutor System with Document Upload
2010
  class RAGAITutor {
2011
  constructor() {
2012
  this.knowledgeBase = [];
2013
  this.vectorStore = null;
2014
  this.isTraining = false;
2015
  this.currentSession = null;
2016
+ this.uploadedDocuments = [];
2017
  this.init();
2018
  }
2019
 
2020
  init() {
2021
  this.setupEventListeners();
2022
+ this.setupDocumentUpload();
2023
  this.loadSampleKnowledge();
2024
  }
2025
 
 
2049
  document.getElementById('trainBtn').addEventListener('click', () => {
2050
  this.trainRAGModel();
2051
  });
2052
+
2053
+ // Enter key in textarea
2054
+ document.getElementById('knowledgeText').addEventListener('keydown', (e) => {
2055
+ if (e.key === 'Enter' && e.ctrlKey) {
2056
+ e.preventDefault();
2057
+ this.addKnowledgeFromText();
2058
+ }
2059
+ });
2060
+ }
2061
+
2062
+ setupDocumentUpload() {
2063
+ const uploadArea = document.getElementById('documentUploadArea');
2064
+ const fileInput = document.getElementById('documentFile');
2065
+ const fileInfo = document.getElementById('fileInfo');
2066
+
2067
+ // Click to upload
2068
+ uploadArea.addEventListener('click', () => {
2069
+ fileInput.click();
2070
+ });
2071
+
2072
+ // Drag and drop
2073
+ uploadArea.addEventListener('dragover', (e) => {
2074
+ e.preventDefault();
2075
+ uploadArea.classList.add('dragover');
2076
+ });
2077
+
2078
+ uploadArea.addEventListener('dragleave', () => {
2079
+ uploadArea.classList.remove('dragover');
2080
+ });
2081
+
2082
+ uploadArea.addEventListener('drop', (e) => {
2083
+ e.preventDefault();
2084
+ uploadArea.classList.remove('dragover');
2085
+
2086
+ if (e.dataTransfer.files.length) {
2087
+ this.handleFiles(e.dataTransfer.files);
2088
+ }
2089
+ });
2090
+
2091
+ // File input change
2092
+ fileInput.addEventListener('change', (e) => {
2093
+ if (e.target.files.length) {
2094
+ this.handleFiles(e.target.files);
2095
+ }
2096
+ });
2097
+ }
2098
+
2099
+ async handleFiles(files) {
2100
+ const fileInfo = document.getElementById('fileInfo');
2101
+ let fileNames = [];
2102
+
2103
+ for (let i = 0; i < files.length; i++) {
2104
+ const file = files[i];
2105
+ fileNames.push(file.name);
2106
+
2107
+ try {
2108
+ const content = await this.readFileContent(file);
2109
+
2110
+ const knowledgeItem = {
2111
+ id: Date.now() + i,
2112
+ title: file.name,
2113
+ content: content,
2114
+ type: "document",
2115
+ fileType: file.type,
2116
+ size: file.size,
2117
+ timestamp: new Date().toISOString()
2118
+ };
2119
+
2120
+ this.addKnowledgeItem(knowledgeItem);
2121
+ this.uploadedDocuments.push({
2122
+ name: file.name,
2123
+ size: this.formatFileSize(file.size),
2124
+ type: file.type
2125
+ });
2126
+
2127
+ } catch (error) {
2128
+ console.error('Error reading file:', error);
2129
+ this.showRAGStatus(`Error reading ${file.name}: ${error.message}`, 'error');
2130
+ }
2131
+ }
2132
+
2133
+ // Show file info
2134
+ fileInfo.innerHTML = `
2135
+ <i class="fas fa-file"></i>
2136
+ Uploaded: ${fileNames.join(', ')}
2137
+ <br>
2138
+ <small>${this.uploadedDocuments.length} documents ready for training</small>
2139
+ `;
2140
+ fileInfo.style.display = 'block';
2141
+
2142
+ this.showRAGStatus(`${fileNames.length} document(s) uploaded successfully!`, 'success');
2143
+
2144
+ // Update visualization
2145
+ if (window.visualization) {
2146
+ window.visualization.setPreset('processing');
2147
+ setTimeout(() => {
2148
+ window.visualization.setPreset('idle');
2149
+ }, 1000);
2150
+ }
2151
+ }
2152
+
2153
+ readFileContent(file) {
2154
+ return new Promise((resolve, reject) => {
2155
+ const reader = new FileReader();
2156
+
2157
+ reader.onload = (e) => {
2158
+ resolve(e.target.result);
2159
+ };
2160
+
2161
+ reader.onerror = (e) => {
2162
+ reject(new Error('Failed to read file'));
2163
+ };
2164
+
2165
+ // Handle different file types
2166
+ if (file.type === 'application/pdf') {
2167
+ // For PDF files, we'll extract text (simplified)
2168
+ reader.readAsArrayBuffer(file);
2169
+ } else if (file.type.includes('text') ||
2170
+ file.name.endsWith('.txt') ||
2171
+ file.name.endsWith('.md')) {
2172
+ reader.readAsText(file);
2173
+ } else if (file.type.includes('document') ||
2174
+ file.name.endsWith('.doc') ||
2175
+ file.name.endsWith('.docx')) {
2176
+ // For Word documents, we'll extract text
2177
+ reader.readAsText(file);
2178
+ } else {
2179
+ reject(new Error('Unsupported file type'));
2180
+ }
2181
+ });
2182
+ }
2183
+
2184
+ formatFileSize(bytes) {
2185
+ if (bytes === 0) return '0 Bytes';
2186
+ const k = 1024;
2187
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
2188
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2189
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
2190
  }
2191
 
2192
  addKnowledgeFromText() {
 
2211
  this.addKnowledgeItem(knowledgeItem);
2212
  textArea.value = '';
2213
  this.showRAGStatus('Knowledge added successfully!', 'success');
2214
+
2215
+ // Update visualization
2216
+ if (window.visualization) {
2217
+ window.visualization.setPreset('processing');
2218
+ setTimeout(() => {
2219
+ window.visualization.setPreset('idle');
2220
+ }, 500);
2221
+ }
2222
  }
2223
 
2224
  addKnowledgeItem(item) {
2225
  this.knowledgeBase.push(item);
2226
+ console.log('Knowledge base updated:', this.knowledgeBase.length, 'items');
2227
  }
2228
 
2229
  async trainRAGModel() {
 
2238
  const progressBar = document.getElementById('trainingProgress');
2239
  progressBar.style.width = '0%';
2240
 
2241
+ const steps = [
2242
+ 'Preprocessing text...',
2243
+ 'Creating embeddings...',
2244
+ 'Building vector store...',
2245
+ 'Optimizing retrieval...',
2246
+ 'Finalizing training...'
2247
+ ];
2248
+
2249
+ // Update visualization for training
2250
+ if (window.visualization) {
2251
+ window.visualization.setPreset('processing');
2252
+ window.visualization.params.intensity = 0.8;
2253
+ }
2254
 
2255
  for (let i = 0; i < steps.length; i++) {
2256
  this.showRAGStatus(steps[i], 'processing');
2257
  progressBar.style.width = `${((i + 1) / steps.length) * 100}%`;
2258
 
2259
+ await this.sleep(800 + Math.random() * 400);
2260
  }
2261
 
2262
  this.vectorStore = {
2263
  size: this.knowledgeBase.length,
2264
  trainedAt: new Date().toISOString(),
2265
+ model: 'all-MiniLM-L6-v2',
2266
+ documents: this.uploadedDocuments.length,
2267
+ textEntries: this.knowledgeBase.filter(k => k.type === 'text').length
2268
  };
2269
 
2270
  this.isTraining = false;
2271
+ this.showRAGStatus(
2272
+ `RAG training complete! ${this.knowledgeBase.length} items indexed.`,
2273
+ 'success'
2274
+ );
2275
+
2276
+ // Update visualization
2277
+ if (window.visualization) {
2278
+ window.visualization.setPreset('teaching');
2279
+ setTimeout(() => {
2280
+ window.visualization.setPreset('idle');
2281
+ window.visualization.params.intensity = 0.5;
2282
+ }, 3000);
2283
+ }
2284
 
2285
  setTimeout(() => {
2286
  progressBar.style.width = '0%';
 
2304
  findRelevantKnowledge(query) {
2305
  const queryLower = query.toLowerCase();
2306
  const relevant = [];
2307
+ const scoredItems = [];
2308
 
2309
+ // Simple keyword matching with scoring
2310
  for (const item of this.knowledgeBase) {
2311
+ let score = 0;
2312
+
2313
+ // Check title
2314
+ if (item.title.toLowerCase().includes(queryLower)) {
2315
+ score += 3;
2316
+ }
2317
+
2318
+ // Check content
2319
+ const contentLower = item.content.toLowerCase();
2320
+ if (contentLower.includes(queryLower)) {
2321
+ score += 2;
2322
+ }
2323
+
2324
+ // Check for partial matches
2325
+ const queryWords = queryLower.split(' ');
2326
+ for (const word of queryWords) {
2327
+ if (word.length > 3 && contentLower.includes(word)) {
2328
+ score += 1;
2329
+ }
2330
+ }
2331
+
2332
+ if (score > 0) {
2333
+ scoredItems.push({ item, score });
2334
  }
2335
  }
2336
 
2337
+ // Sort by score
2338
+ scoredItems.sort((a, b) => b.score - a.score);
2339
+
2340
+ // Return top 3
2341
+ return scoredItems.slice(0, 3).map(s => s.item);
2342
  }
2343
 
2344
  generateResponse(query, relevantKnowledge) {
2345
+ const context = relevantKnowledge.map(item =>
2346
+ `[From: ${item.title}]\n${item.content.substring(0, 300)}${item.content.length > 300 ? '...' : ''}`
2347
+ ).join('\n\n');
2348
+
2349
+ return `Based on my training knowledge:\n\n${context}\n\nRegarding your question "${query}", I can provide detailed explanations from these sources.`;
2350
  }
2351
 
2352
  showRAGStatus(message, type = 'info') {
 
2806
  }
2807
  }
2808
 
2809
+ // 3D Visualization - IMPROVED
2810
  class AITutorVisualization {
2811
  constructor() {
2812
  if (!checkThreeJS()) return;
 
2861
  antialias: true,
2862
  alpha: true
2863
  });
2864
+ this.renderer.setSize(canvas.parentElement.clientWidth, canvas.parentElement.clientHeight);
2865
  this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
2866
 
2867
  this.scene.fog = new THREE.Fog(0x0a0a0f, 10, 25);
 
2873
  directionalLight.position.set(5, 3, 5);
2874
  this.scene.add(directionalLight);
2875
 
2876
+ // Add subtle point lights
2877
+ const pointLight1 = new THREE.PointLight(0x5a6cff, 0.5, 10);
2878
+ pointLight1.position.set(2, 2, 2);
2879
+ this.scene.add(pointLight1);
2880
+
2881
+ const pointLight2 = new THREE.PointLight(0x00ff9d, 0.3, 10);
2882
+ pointLight2.position.set(-2, -1, 3);
2883
+ this.scene.add(pointLight2);
2884
+
2885
  this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
2886
  this.controls.enableDamping = true;
2887
  this.controls.dampingFactor = 0.05;
2888
+ this.controls.maxDistance = 10;
2889
+ this.controls.minDistance = 2;
2890
  }
2891
 
2892
  createParticles() {
 
2894
  const positions = new Float32Array(this.params.particleCount * 3);
2895
  const colors = new Float32Array(this.params.particleCount * 3);
2896
 
2897
+ // Create a more interesting particle distribution
2898
  for (let i = 0; i < this.params.particleCount; i++) {
2899
  const i3 = i * 3;
2900
+
2901
+ // Create particles in a spherical distribution with some noise
2902
+ const radius = 2 + Math.random() * 0.5;
2903
  const phi = Math.acos(-1 + (2 * i) / this.params.particleCount);
2904
  const theta = Math.sqrt(this.params.particleCount * Math.PI) * phi;
2905
 
2906
+ // Add some randomness for more organic look
2907
+ const randomOffset = 0.3;
2908
+ const x = (Math.cos(theta) * Math.sin(phi)) * radius + (Math.random() - 0.5) * randomOffset;
2909
+ const y = (Math.sin(theta) * Math.sin(phi)) * radius + (Math.random() - 0.5) * randomOffset;
2910
+ const z = (Math.cos(phi)) * radius + (Math.random() - 0.5) * randomOffset;
2911
 
2912
  positions[i3] = x;
2913
  positions[i3 + 1] = y;
2914
  positions[i3 + 2] = z;
2915
 
2916
+ // Color based on position for gradient effect
2917
+ const hue = 0.6 + (z / 3) * 0.2; // Blue to purple gradient
2918
+ const saturation = 0.7 + (x / 3) * 0.3;
2919
+ const lightness = 0.5 + (y / 3) * 0.2;
2920
+
2921
+ colors[i3] = hue;
2922
+ colors[i3 + 1] = saturation;
2923
+ colors[i3 + 2] = lightness;
2924
  }
2925
 
2926
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
 
2931
  vertexColors: true,
2932
  transparent: true,
2933
  opacity: 0.8,
2934
+ blending: THREE.AdditiveBlending,
2935
+ sizeAttenuation: true
2936
  });
2937
 
2938
  this.particles = new THREE.Points(geometry, material);
2939
  this.scene.add(this.particles);
2940
 
2941
+ // Add a central core sphere
2942
+ const coreGeometry = new THREE.SphereGeometry(0.3, 16, 16);
2943
+ const coreMaterial = new THREE.MeshBasicMaterial({
2944
  color: 0x5a6cff,
2945
  wireframe: true,
2946
  transparent: true,
2947
+ opacity: 0.2
2948
  });
2949
+ const coreSphere = new THREE.Mesh(coreGeometry, coreMaterial);
2950
+ this.scene.add(coreSphere);
2951
  }
2952
 
2953
  setupControls() {
 
2959
  const particleCountValue = document.getElementById('particleCountValue');
2960
  const energyValue = document.getElementById('energyValue');
2961
 
2962
+ // Set initial values
2963
+ intensityValue.textContent = intensitySlider.value;
2964
+ particleCountValue.textContent = particleCountSlider.value;
2965
+ energyValue.textContent = energySlider.value;
2966
+
2967
  intensitySlider.addEventListener('input', (e) => {
2968
  this.params.intensity = e.target.value / 100;
2969
  intensityValue.textContent = e.target.value;
 
2980
  energyValue.textContent = e.target.value;
2981
  });
2982
 
2983
+ // Preset buttons
2984
  document.querySelectorAll('.preset-btn').forEach(btn => {
2985
  btn.addEventListener('click', (e) => {
2986
  const preset = e.target.dataset.preset;
 
2991
  document.getElementById('retryBtn').addEventListener('click', () => {
2992
  location.reload();
2993
  });
2994
+
2995
+ // Auto-update sliders when preset changes
2996
+ this.setPreset('idle');
2997
  }
2998
 
2999
  setPreset(preset) {
3000
  this.params.mode = preset;
3001
 
3002
+ // Update button styles
3003
  document.querySelectorAll('.preset-btn').forEach(btn => {
3004
  btn.style.background = btn.dataset.preset === preset
3005
  ? 'rgba(90, 108, 255, 0.4)'
3006
  : 'rgba(90, 108, 255, 0.1)';
3007
+ btn.style.transform = btn.dataset.preset === preset
3008
+ ? 'translateY(-2px)'
3009
+ : 'translateY(0)';
3010
  });
3011
+
3012
+ // Update sliders based on preset
3013
+ const sliders = {
3014
+ 'listening': { intensity: 70, energy: 60, particleCount: 2500 },
3015
+ 'processing': { intensity: 85, energy: 80, particleCount: 3000 },
3016
+ 'responding': { intensity: 75, energy: 70, particleCount: 2800 },
3017
+ 'exploring': { intensity: 60, energy: 50, particleCount: 2200 },
3018
+ 'teaching': { intensity: 65, energy: 55, particleCount: 2400 },
3019
+ 'idle': { intensity: 50, energy: 30, particleCount: 2000 }
3020
+ };
3021
+
3022
+ if (sliders[preset]) {
3023
+ const presetValues = sliders[preset];
3024
+
3025
+ document.getElementById('intensity').value = presetValues.intensity;
3026
+ document.getElementById('energy').value = presetValues.energy;
3027
+ document.getElementById('particleCount').value = presetValues.particleCount;
3028
+
3029
+ document.getElementById('intensityValue').textContent = presetValues.intensity;
3030
+ document.getElementById('energyValue').textContent = presetValues.energy;
3031
+ document.getElementById('particleCountValue').textContent = presetValues.particleCount;
3032
+
3033
+ this.params.intensity = presetValues.intensity / 100;
3034
+ this.params.energy = presetValues.energy / 100;
3035
+
3036
+ if (this.params.particleCount !== presetValues.particleCount) {
3037
+ this.params.particleCount = presetValues.particleCount;
3038
+ this.updateParticleCount();
3039
+ }
3040
+ }
3041
  }
3042
 
3043
  updateParticleCount() {
 
3062
  if (!this.particles) return;
3063
 
3064
  const positions = this.particles.geometry.attributes.position.array;
3065
+ const colors = this.particles.geometry.attributes.color.array;
3066
  const originalPositions = this.particles.geometry.attributes.originalPosition;
3067
 
3068
  if (!originalPositions) {
 
3082
 
3083
  let radius = 2.0;
3084
  let intensity = this.params.intensity;
3085
+ let energy = this.params.energy;
3086
 
3087
+ // Different movement patterns based on mode
3088
  switch(this.params.mode) {
3089
  case 'listening':
3090
  radius += Math.sin(time * 3 + i * 0.01) * 0.5 * intensity;
3091
+ radius += Math.cos(time * 2 + i * 0.02) * 0.3 * energy;
3092
  break;
3093
  case 'processing':
3094
  radius += Math.sin(time * 5 + i * 0.02) * 0.6 * intensity;
3095
+ radius += Math.sin(time * 8 + i * 0.01) * 0.4 * energy;
3096
  break;
3097
  case 'responding':
3098
  radius += (Math.sin(time * 2 + i * 0.005) + 1) * 0.4 * intensity;
3099
+ radius += Math.cos(time * 4 + i * 0.01) * 0.3 * energy;
3100
  break;
3101
  case 'exploring':
3102
  radius += Math.sin(time * 1.5 + i * 0.015) * 0.7 * intensity;
3103
+ radius += Math.cos(time * 3 + i * 0.008) * 0.5 * energy;
3104
  break;
3105
  case 'teaching':
3106
  radius += Math.sin(time * 4 + i * 0.008) * 0.55 * intensity;
3107
+ radius += Math.cos(time * 6 + i * 0.012) * 0.4 * energy;
3108
  break;
3109
+ default: // idle
3110
  radius += Math.sin(time * 0.5 + i * 0.01) * 0.2 * intensity;
3111
+ radius += Math.cos(time * 0.3 + i * 0.005) * 0.1 * energy;
3112
  }
3113
 
3114
+ // Add audio-reactive movement
 
3115
  const audioIndex = Math.floor((i / this.params.particleCount) * this.audioData.length);
3116
  radius += this.audioData[audioIndex] * 0.5 * intensity;
3117
 
3118
+ // Apply movement
3119
  positions[i3] = x * radius;
3120
  positions[i3 + 1] = y * radius;
3121
  positions[i3 + 2] = z * radius;
3122
+
3123
+ // Color animation
3124
+ const colorShift = Math.sin(time * 0.5 + i * 0.001) * 0.1;
3125
+ colors[i3] = 0.6 + colorShift * intensity;
3126
  }
3127
 
3128
  this.particles.geometry.attributes.position.needsUpdate = true;
3129
+ this.particles.geometry.attributes.color.needsUpdate = true;
3130
 
3131
+ // Gentle rotation
3132
+ this.particles.rotation.y = time * 0.05;
3133
+ this.particles.rotation.x = Math.sin(time * 0.02) * 0.05;
3134
  }
3135
 
3136
  animate() {
 
3146
  }
3147
 
3148
  onWindowResize() {
3149
+ const canvasContainer = document.getElementById('canvasContainer');
3150
+ const width = canvasContainer.clientWidth;
3151
+ const height = canvasContainer.clientHeight;
3152
+
3153
+ this.camera.aspect = width / height;
3154
  this.camera.updateProjectionMatrix();
3155
+ this.renderer.setSize(width, height);
3156
  }
3157
 
3158
  destroy() {
 
3180
  // Initialize 3D visualization
3181
  window.visualization = new AITutorVisualization();
3182
 
3183
+ // Initialize Enhanced RAG AI Tutor with document upload
3184
  window.ragTutor = new RAGAITutor();
3185
 
3186
+ // Initialize ENHANCED LLM Integration with Apply button
3187
  window.llmIntegration = new LLMIntegration();
3188
 
3189
  // Initialize chat interface with BOTH RAG and LLM
 
3198
  if (window.visualization && window.visualization.scene) {
3199
  window.addEventListener('resize', () => window.visualization.onWindowResize());
3200
 
3201
+ // Add keyboard shortcuts
3202
  window.addEventListener('keydown', (e) => {
3203
  if (e.code === 'Space') {
3204
  window.visualization.setPreset(
 
3208
  if (e.code === 'Space' && e.ctrlKey && window.voiceSynthesis) {
3209
  window.voiceSynthesis.stopSpeaking();
3210
  }
3211
+ if (e.code === 'KeyT' && e.ctrlKey) {
3212
+ const trainingBtn = document.getElementById('trainBtn');
3213
+ if (trainingBtn) trainingBtn.click();
3214
+ }
3215
+ if (e.code === 'KeyL' && e.ctrlKey) {
3216
+ const applyBtn = document.getElementById('applyApiBtn');
3217
+ if (applyBtn) applyBtn.click();
3218
+ }
3219
  });
3220
 
3221
  window.visualization.setPreset('idle');
 
3227
  window.llmIntegration.testConnection().then(connected => {
3228
  if (connected) {
3229
  console.log('LLM API auto-connected on startup');
3230
+ if (window.chatInterface && window.chatInterface.chatStatusText) {
3231
+ window.chatInterface.chatStatusText.textContent = 'Connected to ' + window.llmIntegration.provider;
3232
+ }
3233
  }
3234
  });
3235
  }, 2000);
 
3237
 
3238
  hideLoading();
3239
 
3240
+ // Show welcome message
3241
+ setTimeout(() => {
3242
+ if (window.visualization) {
3243
+ window.visualization.setPreset('exploring');
3244
+ setTimeout(() => {
3245
+ window.visualization.setPreset('idle');
3246
+ }, 2000);
3247
+ }
3248
+ }, 1000);
3249
+
3250
  }, 100);
3251
  });
3252