PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.7
MxChat – AI Chatbot & Content Generation for WordPress v2.5.7
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | js/test-panel.js +64 -214 3.2.172.5.7 View file →
@@ -257,9 +257,9 @@
257 257
258 258 // NEW: Update approved URLs
259 259 this.updateApprovedUrls(testingData.approved_urls || []);
260 260
261 - this.updateTopMatches(testingData.top_matches || [], testingData.similarity_threshold || 0.75, testingData.sources_used || 0, testingData.total_chunks_used || 0);
261 + this.updateTopMatches(testingData.top_matches || [], testingData.similarity_threshold || 0.75);
262 262
263 263 // NEW: Update action matches
264 264 this.updateActionMatches(testingData.action_matches || []);
265 265
@@ -391,173 +391,60 @@
391 391
392 392 actionsEl.innerHTML = html;
393 393 }
394 394
395 - updateTopMatches(topMatches, threshold, sourcesUsed = 0, totalChunksUsed = 0) {
395 + updateTopMatches(topMatches, threshold) {
396 396 const scoresEl = this.panel.querySelector('#similarity-scores');
397 -
397 +
398 398 if (!topMatches || topMatches.length === 0) {
399 399 scoresEl.innerHTML = '<div class="no-data-message">No similarity data available</div>';
400 400 return;
401 401 }
402 -
403 - // Hybrid keyword boost (38ffa1): rows carry matched_via + fused_rank
404 - // when hybrid retrieval was on for this message.
405 - const hybridOn = topMatches.some(m => m.matched_via);
406 -
407 - // Group matches by source URL
408 - const groupedByUrl = {};
409 - topMatches.forEach((match) => {
410 - const url = match.source_display || 'Unknown';
411 - if (!groupedByUrl[url]) {
412 - groupedByUrl[url] = {
413 - url: url,
414 - isUrl: url.startsWith('http'),
415 - bestScore: 0,
416 - usedForContext: false,
417 - totalChunks: match.total_chunks || 1,
418 - matchedChunks: [],
419 - isChunked: match.is_chunk || false,
420 - bestFusedRank: Infinity,
421 - viaSet: {}
422 - };
423 - }
424 -
425 - // Track best score
426 - if (match.similarity_percentage > groupedByUrl[url].bestScore) {
427 - groupedByUrl[url].bestScore = match.similarity_percentage;
428 - }
429 -
430 - // Track if any chunk was used for context
431 - if (match.used_for_context) {
432 - groupedByUrl[url].usedForContext = true;
433 - }
434 -
435 - if (match.fused_rank && match.fused_rank < groupedByUrl[url].bestFusedRank) {
436 - groupedByUrl[url].bestFusedRank = match.fused_rank;
437 - }
438 - if (match.matched_via) {
439 - groupedByUrl[url].viaSet[match.matched_via] = true;
440 - }
441 -
442 - // Add chunk info
443 - groupedByUrl[url].matchedChunks.push({
444 - chunkIndex: match.chunk_index,
445 - score: match.similarity_percentage,
446 - usedForContext: match.used_for_context,
447 - aboveThreshold: match.above_threshold
448 - });
449 - });
450 -
451 - // Convert to array: fused-rank order when hybrid is on, best cosine otherwise
452 - const urlGroups = Object.values(groupedByUrl).sort((a, b) => {
453 - if (hybridOn && a.bestFusedRank !== b.bestFusedRank) {
454 - return a.bestFusedRank - b.bestFusedRank;
455 - }
456 - return b.bestScore - a.bestScore;
457 - });
458 -
459 - // Use backend counts if available, otherwise fall back to frontend calculation
460 - const usedUrlCount = sourcesUsed > 0 ? sourcesUsed : urlGroups.filter(g => g.usedForContext).length;
461 - const chunksInfo = totalChunksUsed > 0 ? `${totalChunksUsed} chunks sent to AI` : `${topMatches.length} chunk matches`;
462 -
402 +
463 403 let html = `<div class="matches-header">
464 - <strong>${usedUrlCount} source${usedUrlCount === 1 ? '' : 's'} used for AI context</strong>
465 - <span class="matches-subheader">(${chunksInfo})</span>
404 + <strong>Top ${topMatches.length} matches</strong>
466 405 </div>`;
467 -
468 - urlGroups.forEach((group, groupIndex) => {
469 - const cardClass = group.usedForContext ? 'above-threshold' : 'below-threshold';
470 - const statusIcon = group.usedForContext ? '✓' : '✗';
471 - const contextLabel = group.usedForContext ? 'Used for AI context' : 'Not used';
472 -
473 - // Build chunk summary
474 - let chunkSummary = '';
475 - if (group.isChunked && group.totalChunks > 1) {
476 - const usedChunkCount = group.matchedChunks.filter(c => c.usedForContext).length;
477 - chunkSummary = `<span class="chunk-summary">${usedChunkCount}/${group.totalChunks} chunks matched</span>`;
406 +
407 + topMatches.forEach((match, index) => {
408 + const isAboveThreshold = match.above_threshold;
409 + const isUsedForContext = match.used_for_context; // Use the actual flag from PHP
410 + const statusIcon = isAboveThreshold ? '✓' : '✗';
411 +
412 + // Determine the correct label based on actual usage
413 + let contextLabel;
414 + if (isUsedForContext) {
415 + contextLabel = 'Used for AI context';
416 + } else if (isAboveThreshold) {
417 + contextLabel = 'Above threshold (not used)';
418 + } else {
419 + contextLabel = 'Below threshold';
478 420 }
479 -
480 - // Check if this entry has multiple matched chunks to show expand toggle
481 - const hasMultipleChunks = group.matchedChunks.length > 1;
482 - const expandToggle = hasMultipleChunks
483 - ? `<span class="chunk-expand-toggle" data-group="${groupIndex}">▶ Show chunks</span>`
484 - : '';
485 -
486 - let viaChip = '';
487 - if (hybridOn) {
488 - const vias = Object.keys(group.viaSet);
489 - if (vias.length) {
490 - const viaLabel = (vias.length > 1 || vias[0] === 'both') ? 'Both'
491 - : (vias[0] === 'keyword' ? 'Keyword' : 'Vector');
492 - viaChip = `<span class="mxch-rag-via-chip mxch-rag-via-${viaLabel.toLowerCase()}">${viaLabel}</span>`;
493 - }
494 - }
495 -
421 +
422 + // Determine card styling - should be based on whether it was actually used
423 + const cardClass = isUsedForContext ? 'above-threshold' : 'below-threshold';
424 +
496 425 html += `
497 426 <div class="match-card ${cardClass}">
498 427 <div class="match-header">
499 428 <div class="match-title">
500 429 <span class="status-icon">${statusIcon}</span>
501 - <span class="similarity-score">${group.bestScore}%</span>
502 - ${viaChip}
503 - ${chunkSummary}
430 + <span class="similarity-score">${match.similarity_percentage}%</span>
504 431 </div>
505 432 <span class="context-label">${contextLabel}</span>
506 433 </div>
507 434 <div class="match-source">
508 - ${group.isUrl ?
509 - `<span class="source-icon link-icon">🔗</span> ${group.url}` :
510 - `<span class="source-icon doc-icon">📄</span> ${group.url}`
435 + ${match.source_display.startsWith('http') ?
436 + `<span class="source-icon link-icon">🔗</span> ${match.source_display}` :
437 + `<span class="source-icon doc-icon">📄</span> ${match.source_display}`
511 438 }
512 439 </div>
513 - ${expandToggle}
514 - ${hasMultipleChunks ? this.renderChunkDetails(group.matchedChunks, groupIndex) : ''}
515 440 </div>
516 441 `;
517 442 });
518 -
443 +
519 444 scoresEl.innerHTML = html;
520 -
521 - // Add click handlers for expand toggles
522 - scoresEl.querySelectorAll('.chunk-expand-toggle').forEach(toggle => {
523 - toggle.addEventListener('click', (e) => {
524 - const groupId = e.target.dataset.group;
525 - const details = scoresEl.querySelector(`.chunk-details[data-group="${groupId}"]`);
526 - if (details) {
527 - const isExpanded = details.classList.toggle('expanded');
528 - e.target.textContent = isExpanded ? '▼ Hide chunks' : '▶ Show chunks';
529 - }
530 - });
531 - });
532 445 }
533 446
534 - renderChunkDetails(chunks, groupIndex) {
535 - // Sort chunks by chunk index
536 - const sortedChunks = [...chunks].sort((a, b) => (a.chunkIndex || 0) - (b.chunkIndex || 0));
537 -
538 - let html = `<div class="chunk-details" data-group="${groupIndex}">`;
539 -
540 - sortedChunks.forEach(chunk => {
541 - const chunkNum = (chunk.chunkIndex !== null && chunk.chunkIndex !== undefined)
542 - ? chunk.chunkIndex + 1
543 - : '?';
544 - const statusClass = chunk.usedForContext ? 'chunk-used' : 'chunk-not-used';
545 - const statusIcon = chunk.usedForContext ? '✓' : '○';
546 -
547 - html += `
548 - <div class="chunk-detail-row ${statusClass}">
549 - <span class="chunk-detail-icon">${statusIcon}</span>
550 - <span class="chunk-detail-num">Chunk ${chunkNum}</span>
551 - <span class="chunk-detail-score">${chunk.score}%</span>
552 - </div>
553 - `;
554 - });
555 -
556 - html += '</div>';
557 - return html;
558 - }
559 -
560 447 updateLastQuery(query, topMatches) {
561 448 const queryEl = this.panel.querySelector('#last-query');
562 449 queryEl.textContent = query;
563 450
@@ -568,49 +455,28 @@
568 455 };
569 456 }
570 457
571 458 clearChatSession() {
572 - // Determine the active bot ID from MxChatInstances
573 - let botId = 'default';
574 - if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getAllBotIds === 'function') {
575 - const botIds = MxChatInstances.getAllBotIds();
576 - if (botIds.length > 0) {
577 - botId = botIds[0];
578 - }
579 - }
580 -
581 - // Get current session ID using the correct bot-suffixed cookie name
582 - const cookieName = 'mxchat_session_id_' + botId;
583 - const sessionId = this.getCookie(cookieName) || this.getCookie('mxchat_session_id') || this.getCurrentSessionId();
584 -
459 + // Get current session ID from cookie (most reliable)
460 + const sessionId = this.getCookie('mxchat_session_id') || this.getCurrentSessionId();
461 +
585 462 if (!sessionId) {
586 - this.log('No active session found');
463 + this.log('❌ No active session found');
587 464 return;
588 465 }
589 -
590 - this.log('Current session ID: ' + sessionId);
591 - this.log('Starting fresh session...');
592 -
593 - // Use MxChatInstances.resetChatSession to properly reset in-memory state + cookie
594 - if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.resetChatSession === 'function') {
595 - MxChatInstances.resetChatSession(botId);
596 - }
597 -
598 - // Get the new session ID that was just set by resetChatSession
599 - let newSessionId = '';
600 - if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getChatSession === 'function') {
601 - newSessionId = MxChatInstances.getChatSession(botId);
602 - }
603 -
604 - // Fallback if MxChatInstances wasn't available
605 - if (!newSessionId) {
606 - newSessionId = 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
607 - this.clearMxChatCookie(botId);
608 - this.setChatSession(newSessionId, botId);
609 - }
610 -
611 - this.log('New session ID: ' + newSessionId);
612 -
466 +
467 + this.log(`🔍 Current session ID: ${sessionId}`);
468 + this.log('🧹 Starting fresh session...');
469 +
470 + // Generate new session ID (using your existing format)
471 + const newSessionId = 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
472 +
473 + // Clear the old cookie and set new one immediately
474 + this.clearMxChatCookie();
475 + this.setChatSession(newSessionId);
476 +
477 + this.log(`🆕 New session ID: ${newSessionId}`);
478 +
613 479 // Call backend to clear old session data
614 480 fetch(mxchatTestData.ajaxUrl, {
615 481 method: 'POST',
616 482 headers: {
@@ -625,36 +491,36 @@
625 491 })
626 492 .then(response => response.json())
627 493 .then(data => {
628 494 if (data.success) {
629 - this.log('Backend session cleared: ' + data.data.message);
630 -
495 + this.log('✅ Backend session cleared: ' + data.data.message);
496 +
631 497 // Update the session ID everywhere in the DOM
632 498 this.updateSessionIdEverywhere(newSessionId);
633 -
499 +
634 500 // Clear the chat UI
635 501 this.clearChatUI();
636 -
502 +
637 503 // Show popular questions again
638 504 const popularQuestions = document.querySelector('#mxchat-popular-questions');
639 505 if (popularQuestions) {
640 506 popularQuestions.style.display = 'block';
641 507 }
642 -
508 +
643 509 // Clear testing data displays
644 510 this.updateLastQuery('New session started', []);
645 511 this.updateTopMatches([], 0);
646 512 this.updateApprovedUrls([]);
647 -
648 - this.log('Fresh session started successfully');
649 -
513 +
514 + this.log('🎉 Fresh session started successfully');
515 +
650 516 } else {
651 - this.log('Error clearing session: ' + (data.data?.message || 'Unknown error'));
517 + this.log('❌ Error clearing session: ' + (data.data?.message || 'Unknown error'));
652 518 }
653 519 })
654 520 .catch(error => {
655 521 console.error('Error clearing chat session:', error);
656 - this.log('Connection error when clearing session');
522 + this.log('🔌 Connection error when clearing session');
657 523 });
658 524 }
659 525
660 526 // Helper function to get cookie (same as your existing one)
@@ -663,21 +529,19 @@
663 529 let parts = value.split("; " + name + "=");
664 530 if (parts.length == 2) return parts.pop().split(";").shift();
665 531 }
666 532
667 - // Helper function to set session cookie (matches chat-script.js format)
668 - setChatSession(sessionId, botId) {
669 - botId = botId || 'default';
670 - document.cookie = 'mxchat_session_id_' + botId + '=' + sessionId + '; path=/; max-age=86400; SameSite=Lax';
533 + // Helper function to set session cookie (same as your existing one)
534 + setChatSession(sessionId) {
535 + // Set the cookie with a 24-hour expiration (86400 seconds)
536 + document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
671 537 }
672 538
673 539 // Helper function to clear the MxChat session cookie
674 - clearMxChatCookie(botId) {
675 - botId = botId || 'default';
676 - // Clear both bot-suffixed and legacy cookie formats
677 - document.cookie = 'mxchat_session_id_' + botId + '=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
678 - document.cookie = 'mxchat_session_id=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
679 - this.log('Session cookie cleared');
540 + clearMxChatCookie() {
541 + // Clear the cookie by setting it to expire in the past
542 + document.cookie = "mxchat_session_id=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax";
543 + this.log('🍪 Session cookie cleared');
680 544 }
681 545
682 546 updateSessionIdEverywhere(newSessionId) {
683 547 // Update global session ID variable if it exists
@@ -736,23 +600,9 @@
736 600 }
737 601 }
738 602
739 603 getCurrentSessionId() {
740 - // Try MxChatInstances first (most reliable — matches chat-script.js)
741 - if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getChatSession === 'function') {
742 - const botIds = typeof MxChatInstances.getAllBotIds === 'function' ? MxChatInstances.getAllBotIds() : ['default'];
743 - const botId = botIds.length > 0 ? botIds[0] : 'default';
744 - const instanceSession = MxChatInstances.getChatSession(botId);
745 - if (instanceSession) {
746 - return instanceSession;
747 - }
748 - }
749 -
750 - // Try bot-suffixed cookie, then legacy cookie
751 - const botCookieId = this.getCookie('mxchat_session_id_default');
752 - if (botCookieId) {
753 - return botCookieId;
754 - }
604 + // First try to get from cookie (most reliable)
755 605 const cookieSessionId = this.getCookie('mxchat_session_id');
756 606 if (cookieSessionId) {
757 607 return cookieSessionId;
758 608 }