| @@ -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 + '">▶ 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>' + |