PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.0
MxChat – AI Chatbot & Content Generation for WordPress v3.2.0
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/admin-testing-tab.js +6 -96 3.2.213.2.0 View file →
@@ -76,9 +76,9 @@
76 76 interceptChatResponses: function() {
77 77 var self = this;
78 78
79 79 // Store reference globally so interception can find us
80 - window.mxchatTestPanelInstance = self;
80 + window.mxchatAdminTestPanel = self;
81 81
82 82 // Intercept jQuery AJAX calls
83 83 if (window.jQuery) {
84 84 var originalAjax = jQuery.ajax;
@@ -107,9 +107,9 @@
107 107 return originalAjax.call(this, options);
108 108 };
109 109 }
110 110
111 - // Intercept fetch API calls (for both JSON and SSE streaming)
111 + // Intercept fetch API calls (for streaming)
112 112 var originalFetch = window.fetch;
113 113
114 114 window.fetch = function() {
115 115 var args = arguments;
@@ -115,12 +115,10 @@
115 115 var args = arguments;
116 116 return originalFetch.apply(this, args).then(function(response) {
117 117 if (args[0] && typeof args[0] === 'string' &&
118 118 (args[0].includes('admin-ajax.php') || args[0].includes('mxchat'))) {
119 - var contentType = response.headers.get('content-type') || '';
120 -
121 - if (contentType.includes('application/json')) {
122 - // Non-streaming: parse the full JSON response
119 + var contentType = response.headers.get('content-type');
120 + if (contentType && contentType.includes('application/json')) {
123 121 response.clone().json().then(function(data) {
124 122 if (self && data && data.testing_data) {
125 123 self.handleTestingData(data.testing_data);
126 124 } else if (self && data && data.data && data.data.testing_data) {
@@ -126,57 +124,8 @@
126 124 } else if (self && data && data.data && data.data.testing_data) {
127 125 self.handleTestingData(data.data.testing_data);
128 126 }
129 127 }).catch(function() {});
130 - } else if (contentType.includes('text/event-stream') || contentType.includes('text/plain')) {
131 - // Streaming (SSE): read just enough to find the testing_data event
132 - // Using clone() so the original stream is unaffected for chat-script.js
133 - try {
134 - var clonedResponse = response.clone();
135 - var reader = clonedResponse.body.getReader();
136 - var decoder = new TextDecoder();
137 - var sseBuffer = '';
138 - var found = false;
139 -
140 - function readChunk() {
141 - reader.read().then(function(result) {
142 - if (result.done || found) {
143 - reader.cancel().catch(function() {});
144 - return;
145 - }
146 -
147 - sseBuffer += decoder.decode(result.value, { stream: true });
148 - var sseLines = sseBuffer.split('\n');
149 -
150 - for (var i = 0; i < sseLines.length; i++) {
151 - var sseLine = sseLines[i];
152 - if (sseLine.indexOf('data: ') === 0) {
153 - var payload = sseLine.substring(6);
154 - try {
155 - var parsed = JSON.parse(payload);
156 - if (parsed && parsed.testing_data) {
157 - self.handleTestingData(parsed.testing_data);
158 - found = true;
159 - reader.cancel().catch(function() {});
160 - return;
161 - }
162 - } catch (e) {}
163 - }
164 - }
165 -
166 - // Keep reading until we find testing_data or hit a limit
167 - if (sseBuffer.length < 50000) {
168 - readChunk();
169 - } else {
170 - reader.cancel().catch(function() {});
171 - }
172 - }).catch(function() {
173 - // Stream read error — safe to ignore
174 - });
175 - }
176 -
177 - readChunk();
178 - } catch (e) {}
179 128 }
180 129 }
181 130 return response;
182 131 });
@@ -209,18 +158,8 @@
209 158 // Update action matches
210 159 this.updateActionMatches(testingData.action_matches || []);
211 160
212 161 // Log summary
213 - // 58f8b4: URL-guard outcome — surface silent stripping.
214 - if (testingData.url_validation) {
215 - var uv = testingData.url_validation;
216 - if (uv.removed_count > 0) {
217 - this.log('URL guard stripped ' + uv.removed_count + ' unapproved link' + (uv.removed_count !== 1 ? 's' : '') + ': ' + (uv.removed_urls || []).join(', '));
218 - } else if (uv.checked > 0) {
219 - this.log('URL guard: all ' + uv.checked + ' link' + (uv.checked !== 1 ? 's' : '') + ' in the answer approved (' + (uv.strict ? 'strict' : 'lenient') + ' mode)');
220 - }
221 - }
222 -
223 162 if (testingData.knowledge_base_type) {
224 163 this.log('Knowledge Base: ' + testingData.knowledge_base_type);
225 164 }
226 165 if (testingData.similarity_threshold) {
@@ -328,12 +267,8 @@
328 267 el.html('<div class="mxch-testing-no-data">No similarity data available</div>');
329 268 return;
330 269 }
331 270
332 - // Hybrid keyword boost (38ffa1): rows carry matched_via + fused_rank
333 - // when hybrid retrieval was on for this test message.
334 - var hybridOn = topMatches.some(function(m) { return m.matched_via; });
335 -
336 271 // Group matches by source URL
337 272 var groupedByUrl = {};
338 273 topMatches.forEach(function(match) {
339 274 var url = match.source_display || 'Unknown';
@@ -344,11 +279,9 @@
344 279 bestScore: 0,
345 280 usedForContext: false,
346 281 totalChunks: match.total_chunks || 1,
347 282 matchedChunks: [],
348 - isChunked: match.is_chunk || false,
349 - bestFusedRank: Infinity,
350 - viaSet: {}
283 + isChunked: match.is_chunk || false
351 284 };
352 285 }
353 286
354 287 if (match.similarity_percentage > groupedByUrl[url].bestScore) {
@@ -358,15 +291,8 @@
358 291 if (match.used_for_context) {
359 292 groupedByUrl[url].usedForContext = true;
360 293 }
361 294
362 - if (match.fused_rank && match.fused_rank < groupedByUrl[url].bestFusedRank) {
363 - groupedByUrl[url].bestFusedRank = match.fused_rank;
364 - }
365 - if (match.matched_via) {
366 - groupedByUrl[url].viaSet[match.matched_via] = true;
367 - }
368 -
369 295 groupedByUrl[url].matchedChunks.push({
370 296 chunkIndex: match.chunk_index,
371 297 score: match.similarity_percentage,
372 298 usedForContext: match.used_for_context,
@@ -373,14 +299,9 @@
373 299 aboveThreshold: match.above_threshold
374 300 });
375 301 });
376 302
377 - var urlGroups = Object.values(groupedByUrl).sort(function(a, b) {
378 - if (hybridOn && a.bestFusedRank !== b.bestFusedRank) {
379 - return a.bestFusedRank - b.bestFusedRank;
380 - }
381 - return b.bestScore - a.bestScore;
382 - });
303 + var urlGroups = Object.values(groupedByUrl).sort(function(a, b) { return b.bestScore - a.bestScore; });
383 304 var usedUrlCount = sourcesUsed > 0 ? sourcesUsed : urlGroups.filter(function(g) { return g.usedForContext; }).length;
384 305 var chunksInfo = totalChunksUsed > 0 ? totalChunksUsed + ' chunks sent to AI' : topMatches.length + ' chunk matches';
385 306
386 307 var html = '<div class="matches-header">' +
@@ -405,24 +326,13 @@
405 326 var expandToggle = hasMultipleChunks
406 327 ? '<span class="chunk-expand-toggle" data-group="' + groupIndex + '">&#9654; Show chunks</span>'
407 328 : '';
408 329
409 - var viaChip = '';
410 - if (hybridOn) {
411 - var vias = Object.keys(group.viaSet);
412 - if (vias.length) {
413 - var viaLabel = (vias.length > 1 || vias[0] === 'both') ? 'Both'
414 - : (vias[0] === 'keyword' ? 'Keyword' : 'Vector');
415 - viaChip = '<span class="mxch-rag-via-chip mxch-rag-via-' + viaLabel.toLowerCase() + '">' + viaLabel + '</span>';
416 - }
417 - }
418 -
419 330 html += '<div class="match-card ' + cardClass + '">' +
420 331 '<div class="match-header">' +
421 332 '<div class="match-title">' +
422 333 '<span class="status-icon">' + statusIcon + '</span>' +
423 334 '<span class="similarity-score">' + group.bestScore + '%</span>' +
424 - viaChip +
425 335 chunkSummary +
426 336 '</div>' +
427 337 '<span class="context-label">' + contextLabel + '</span>' +
428 338 '</div>' +