Changeset 3414458
- Timestamp:
- 12/08/2025 02:56:22 PM (2 months ago)
- Location:
- askeet/trunk
- Files:
-
- 3 edited
-
askeet.php (modified) (8 diffs)
-
assets/css/style.css (modified) (3 diffs)
-
assets/js/script.js (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
askeet/trunk/askeet.php
r3414259 r3414458 217 217 'ai_fixing_query' => __('The assistant is trying to fix the query...', 'askeet'), 218 218 'ai_failed_3_times' => __('Unable to process the request, please reformulate your question.', 'askeet'), 219 'ai_failed_5_times' => __('Unable to process the request after 5 attempts, please reformulate your question.', 'askeet'),219 'ai_failed_5_times' => __('Unable to process the request, please reformulate your question.', 'askeet'), 220 220 'ai_retrying' => __('Retrying...', 'askeet'), 221 221 'ai_warning' => __('Askeet is an AI and may make mistakes. Please verify the responses.', 'askeet'), … … 418 418 $sql_error = isset($data['sql_error']) ? sanitize_textarea_field(wp_unslash($data['sql_error'])) : ''; 419 419 $retry_attempt = isset($data['retry_attempt']) ? intval($data['retry_attempt']) : 0; 420 $query_id = isset($data['query_id']) ? intval($data['query_id']) : null; 420 421 } else { 421 422 $query_request = isset($_POST['query_request']) ? sanitize_textarea_field(wp_unslash($_POST['query_request'])) : ''; … … 426 427 $sql_error = isset($_POST['sql_error']) ? sanitize_textarea_field(wp_unslash($_POST['sql_error'])) : ''; 427 428 $retry_attempt = isset($_POST['retry_attempt']) ? intval($_POST['retry_attempt']) : 0; 429 $query_id = isset($_POST['query_id']) ? intval($_POST['query_id']) : null; 428 430 } 429 431 $db_structure = $this->askeet_get_full_db_structure(); … … 439 441 'sql_error' => $sql_error, 440 442 'retry_attempt' => $retry_attempt, 443 'query_id' => $query_id, 441 444 ); 445 // Debug logging - can be removed in production 446 error_log('[ASKEET PHP DEBUG] Sending to API: retry_attempt=' . $retry_attempt . ', query_id=' . ($query_id !== null ? $query_id : 'null')); 442 447 $response = wp_remote_post($full_url, array( 443 448 'timeout' => 60, … … 486 491 // PATCH: Detect daily limit and add current_plan from backend 487 492 if (isset($data['error']) && (stripos($data['error'], 'daily limit') !== false)) { 488 wp_send_json_error(array('message' => $data['error'], 'current_plan' => isset($data['current_plan']) ? $data['current_plan'] : '')); 493 wp_send_json_error(array( 494 'message' => $data['error'], 495 'current_plan' => isset($data['current_plan']) ? $data['current_plan'] : '', 496 'sql_query' => isset($data['sql_query']) ? $data['sql_query'] : '', 497 'query_id' => isset($data['query_id']) ? $data['query_id'] : null // Include query_id for retry tracking 498 )); 489 499 exit; 490 500 } 491 wp_send_json_error(array('message' => isset($data['error']) ? $data['error'] : __('Erreur de traitement de la réponse IA.', 'askeet'))); 501 wp_send_json_error(array( 502 'message' => isset($data['error']) ? $data['error'] : __('Erreur de traitement de la réponse IA.', 'askeet'), 503 'sql_query' => isset($data['sql_query']) ? $data['sql_query'] : '', // Include sql_query for retry context 504 'query_id' => isset($data['query_id']) ? $data['query_id'] : null // Include query_id for retry tracking 505 )); 492 506 exit; 493 507 } … … 495 509 $sql_query = preg_replace('/\s+/', ' ', $sql_query); 496 510 if (empty($sql_query)) { 497 wp_send_json_error(['message' => __('Impossible de traiter la demande, veuillez reformuler votre question.', 'askeet')]); 511 wp_send_json_error([ 512 'message' => __('Impossible de traiter la demande, veuillez reformuler votre question.', 'askeet'), 513 'sql_query' => '', // Include empty sql_query for retry context 514 'query_id' => isset($data['query_id']) ? $data['query_id'] : null // Include query_id for retry tracking 515 ]); 498 516 exit; 499 517 } 500 518 // Vérification de sécurité : seules les requêtes SELECT sont autorisées 501 519 if (!$this->askeet_is_safe_query($sql_query)) { 502 wp_send_json_error(['message' => 'Seules les requêtes SELECT sont autorisées.']); 520 wp_send_json_error([ 521 'message' => 'Seules les requêtes SELECT sont autorisées.', 522 'sql_query' => $sql_query, // Include sql_query for retry context 523 'query_id' => isset($data['query_id']) ? $data['query_id'] : null // Include query_id for retry tracking 524 ]); 503 525 exit; 504 526 } … … 517 539 } 518 540 if ($error) { 519 wp_send_json_error(array('message' => __('Erreur SQL: ', 'askeet') . $error)); 541 wp_send_json_error(array( 542 'message' => __('Erreur SQL: ', 'askeet') . $error, 543 'sql_query' => $sql_query, 544 'query_id' => isset($data['query_id']) ? $data['query_id'] : null 545 )); 520 546 exit; 521 547 } … … 523 549 if (empty($results)) { 524 550 } 525 wp_send_json_success(array('results' => $results, 'sql_query' => $sql_query)); 551 wp_send_json_success(array( 552 'results' => $results, 553 'sql_query' => $sql_query, 554 'query_id' => isset($data['query_id']) ? $data['query_id'] : null 555 )); 526 556 exit; 527 557 } -
askeet/trunk/assets/css/style.css
r3414259 r3414458 84 84 } 85 85 86 /* Specific button overrides for WordPress admin compatibility */ 86 87 #btn-discord, 87 88 #btn-slack, 88 #btn-support { 89 #btn-support, 90 .askeet-guide-wrapper #btn-discord, 91 .askeet-guide-wrapper #btn-slack, 92 .askeet-guide-wrapper #btn-support, 93 .askeet-guide-wrapper .button-group a.btn, 94 .askeet-guide-wrapper .button-group .btn, 95 .wrap.askeet .button-group a.btn, 96 .wrap.askeet .button-group .btn, 97 body.wp-admin #btn-discord, 98 body.wp-admin #btn-slack, 99 body.wp-admin #btn-support { 89 100 color: #ffffff !important; 90 101 background: linear-gradient(135deg, #005a9e 0%, #0077cc 100%) !important; 102 -webkit-text-fill-color: #ffffff !important; 103 text-shadow: none !important; 104 } 105 106 #btn-discord:visited, 107 #btn-slack:visited, 108 #btn-support:visited, 109 .askeet-guide-wrapper a.btn:visited, 110 .button-group a.btn:visited { 111 color: #ffffff !important; 112 -webkit-text-fill-color: #ffffff !important; 91 113 } 92 114 … … 1572 1594 1573 1595 .wcqa-retry-query.button { 1574 background: linear-gradient(135deg, # ff6b6b 0%, #ee5a5a100%) !important;1596 background: linear-gradient(135deg, #6c757d 0%, #5a6268 100%) !important; 1575 1597 color: #fff !important; 1576 1598 border: none !important; … … 1581 1603 cursor: pointer !important; 1582 1604 transition: all 0.3s ease !important; 1583 box-shadow: 0 2px 8px rgba( 255, 107, 107, 0.3) !important;1605 box-shadow: 0 2px 8px rgba(108, 117, 125, 0.3) !important; 1584 1606 } 1585 1607 1586 1608 .wcqa-retry-query.button:hover { 1587 background: linear-gradient(135deg, # ff5252 0%, #e53935100%) !important;1609 background: linear-gradient(135deg, #5a6268 0%, #495057 100%) !important; 1588 1610 transform: translateY(-1px) !important; 1589 box-shadow: 0 4px 12px rgba( 255, 107, 107, 0.4) !important;1611 box-shadow: 0 4px 12px rgba(108, 117, 125, 0.4) !important; 1590 1612 } 1591 1613 -
askeet/trunk/assets/js/script.js
r3414259 r3414458 14 14 let lastQueryLastSql = ''; 15 15 let lastQueryLastColumns = []; 16 let executionRetryCount = 0; // Track retries for execution logging 17 let currentQueryId = null; // Track query_id for database updates on retries 16 18 17 19 // Helper: scroll chat to bottom … … 218 220 lastQueryRequest = queryRequest; 219 221 lastQueryChatHistory = JSON.parse(JSON.stringify(chatHistory)); 222 currentQueryId = null; // Reset query_id for new query 223 executionRetryCount = 0; // Reset execution retry count 220 224 renderMessage(null, 'user', queryRequest); 221 225 $('#query-request').val(''); … … 239 243 }; 240 244 241 // If this is a retry, include the failed query and errorfor AI context242 if (attempt > 1 && lastFailedSqlQuery) {243 requestData.failed_sql_query = lastFailedSqlQuery ;244 requestData.sql_error = lastErrorMessage ;245 // If this is a retry, include the failed query, error and query_id for AI context 246 if (attempt > 1 && (lastFailedSqlQuery || lastErrorMessage)) { 247 requestData.failed_sql_query = lastFailedSqlQuery || ''; 248 requestData.sql_error = lastErrorMessage || ''; 245 249 requestData.retry_attempt = attempt; 250 if (currentQueryId) { 251 requestData.query_id = currentQueryId; 252 } 253 console.log('[RETRY DEBUG] Sending retry - attempt:', attempt, 'currentQueryId:', currentQueryId, 'lastFailedSqlQuery:', !!lastFailedSqlQuery, 'lastErrorMessage:', !!lastErrorMessage); 254 } else { 255 console.log('[QUERY DEBUG] First attempt - attempt:', attempt, 'currentQueryId:', currentQueryId); 246 256 } 247 257 … … 260 270 let sql_query = (response.data && response.data.sql_query) ? response.data.sql_query : (response.sql_query ? response.sql_query : ''); 261 271 currentSqlQuery = sql_query || ''; 272 273 // Store query_id for tracking (from first response) 274 let responseQueryId = (response.data && response.data.query_id) ? response.data.query_id : null; 275 console.log('[RESPONSE DEBUG] response.success:', response.success, 'responseQueryId:', responseQueryId, 'currentQueryId before:', currentQueryId); 276 if (responseQueryId && !currentQueryId) { 277 currentQueryId = responseQueryId; 278 console.log('[QUERY] Got query_id:', currentQueryId); 279 } 280 262 281 // Only execute the paginated query, do NOT render SQL or button 263 282 if (response.success && currentSqlQuery) { … … 273 292 } else { 274 293 // Store failed query info for next retry 275 if (sql_query) { 276 lastFailedSqlQuery = sql_query; 294 // Capture SQL query even on failure if available 295 let failedQuery = sql_query || 296 (response.data && response.data.sql_query) || 297 (response.sql_query) || ''; 298 if (failedQuery) { 299 lastFailedSqlQuery = failedQuery; 277 300 } 278 lastErrorMessage = (response.data && response.data.message) ? response.data.message : 'Query failed'; 301 302 // Capture query_id from error response for retry tracking 303 let errorQueryId = (response.data && response.data.query_id) ? response.data.query_id : null; 304 if (errorQueryId && !currentQueryId) { 305 currentQueryId = errorQueryId; 306 console.log('[RETRY] Got query_id from error response:', currentQueryId); 307 } 308 309 // Get error message from response 310 lastErrorMessage = (response.data && response.data.message) ? response.data.message : 311 (response.message) ? response.message : 'Query failed - AI could not generate valid SQL'; 312 313 console.log('[RETRY] Attempt', attempt, 'failed. SQL:', lastFailedSqlQuery, 'Error:', lastErrorMessage, 'query_id:', currentQueryId); 279 314 280 315 // Check if it's a non-retryable error (like security restriction) … … 283 318 284 319 if (isNonRetryableError || attempt >= maxAttempts) { 285 // Max attempts reached or non-retryable error, show final error 320 // Max attempts reached or non-retryable error - log final failure 321 logExecutionResult(false, lastFailedSqlQuery, lastErrorMessage, attempt); 322 executionRetryCount = 0; // Reset 323 286 324 removeLoading(); 287 325 $('#process-query').prop('disabled', false); … … 315 353 lastErrorMessage = resp.data.message; 316 354 } 355 // Capture SQL query from error response if available 356 if (resp && resp.data && resp.data.sql_query) { 357 lastFailedSqlQuery = resp.data.sql_query; 358 } 317 359 } catch(e){} 318 360 361 console.log('[RETRY] Attempt', attempt, 'error. Error:', lastErrorMessage); 362 319 363 if (attempt >= maxAttempts) { 320 // Max attempts reached, show final error 364 // Max attempts reached - log final failure 365 logExecutionResult(false, lastFailedSqlQuery, 'Network error: ' + lastErrorMessage, attempt); 366 executionRetryCount = 0; // Reset 367 321 368 removeLoading(); 322 369 $('#process-query').prop('disabled', false); … … 362 409 363 410 // If this is a retry, include the failed query and error for AI context 364 if (attempt > 1 && lastFailedSqlQuery) {365 requestData.failed_sql_query = lastFailedSqlQuery ;366 requestData.sql_error = lastErrorMessage ;411 if (attempt > 1 && (lastFailedSqlQuery || lastErrorMessage)) { 412 requestData.failed_sql_query = lastFailedSqlQuery || ''; 413 requestData.sql_error = lastErrorMessage || ''; 367 414 requestData.retry_attempt = attempt; 368 415 } … … 392 439 } else { 393 440 // Store failed query info for next retry 394 if (sql_query) { 395 lastFailedSqlQuery = sql_query; 441 // Capture SQL query even on failure if available 442 let failedQuery = sql_query || 443 (response.data && response.data.sql_query) || 444 (response.sql_query) || ''; 445 if (failedQuery) { 446 lastFailedSqlQuery = failedQuery; 396 447 } 397 lastErrorMessage = (response.data && response.data.message) ? response.data.message : 'Query failed'; 448 449 // Get error message from response 450 lastErrorMessage = (response.data && response.data.message) ? response.data.message : 451 (response.message) ? response.message : 'Query failed - AI could not generate valid SQL'; 452 453 console.log('[RETRY-LAST] Attempt', attempt, 'failed. SQL:', lastFailedSqlQuery, 'Error:', lastErrorMessage); 398 454 399 455 // Check if it's a non-retryable error (like security restriction) … … 402 458 403 459 if (isNonRetryableError || attempt >= maxAttempts) { 404 // Max attempts reached or non-retryable error, show final error 460 // Max attempts reached or non-retryable error - log final failure 461 logExecutionResult(false, lastFailedSqlQuery, lastErrorMessage, attempt); 462 executionRetryCount = 0; // Reset 463 405 464 removeLoading(); 406 465 $('#process-query').prop('disabled', false); … … 434 493 lastErrorMessage = resp.data.message; 435 494 } 495 // Capture SQL query from error response if available 496 if (resp && resp.data && resp.data.sql_query) { 497 lastFailedSqlQuery = resp.data.sql_query; 498 } 436 499 } catch(e){} 437 500 501 console.log('[RETRY-LAST] Attempt', attempt, 'error. Error:', lastErrorMessage); 502 438 503 if (attempt >= maxAttempts) { 439 // Max attempts reached, show final error 504 // Max attempts reached - log final failure 505 logExecutionResult(false, lastFailedSqlQuery, 'Network error: ' + lastErrorMessage, attempt); 506 executionRetryCount = 0; // Reset 507 440 508 removeLoading(); 441 509 $('#process-query').prop('disabled', false); … … 450 518 } 451 519 tryProcessQuery(); 520 } 521 522 // Retry with execution error context - called when SQL execution fails 523 function retryWithExecutionError(originalQuery, failedSql, executionError) { 524 console.log('[RETRY EXEC] Starting retry with execution error context'); 525 console.log('[RETRY EXEC] Original query:', originalQuery); 526 console.log('[RETRY EXEC] Failed SQL:', failedSql); 527 console.log('[RETRY EXEC] Execution error:', executionError); 528 529 // Show loading 530 renderLoading(); 531 $('#process-query').prop('disabled', true); 532 533 let attempt = 0; 534 const maxAttempts = 5; 535 let lastFailedSqlQuery = failedSql; 536 let lastErrorMessage = executionError; 537 538 function tryProcessQueryWithContext() { 539 attempt++; 540 console.log('[RETRY EXEC] Attempt', attempt, 'of', maxAttempts); 541 542 // Always include error context since we're retrying due to execution error 543 let requestData = { 544 action: 'askeet_process_query_request', 545 query_request: originalQuery, 546 failed_sql_query: lastFailedSqlQuery, 547 sql_error: lastErrorMessage, 548 retry_attempt: attempt, 549 nonce: askeet_query_assistant.nonce, 550 install_id: askeet_query_assistant.install_id 551 }; 552 553 // Include query_id for database tracking 554 if (currentQueryId) { 555 requestData.query_id = currentQueryId; 556 } 557 558 console.log('[RETRY EXEC DEBUG] query_id being sent:', currentQueryId, 'retry_attempt:', attempt); 559 560 $.ajax({ 561 url: askeet_query_assistant.ajax_url, 562 type: 'POST', 563 data: requestData, 564 success: function(response) { 565 if (handleLimitModals(response)) { 566 removeLoading(); 567 $('#process-query').prop('disabled', false); 568 return; 569 } 570 571 let sql_query = (response.data && response.data.sql_query) ? response.data.sql_query : (response.sql_query ? response.sql_query : ''); 572 currentSqlQuery = sql_query || ''; 573 574 if (response.success && currentSqlQuery) { 575 console.log('[RETRY EXEC] Got new SQL, executing:', currentSqlQuery); 576 // Try to execute the new query 577 executeQueryPage(1, true); 578 } else if (response.success) { 579 removeLoading(); 580 $('#process-query').prop('disabled', false); 581 renderMessage('<div class="query-success">'+askeet_query_assistant_i18n.query_executed_successfully+' 0 '+askeet_query_assistant_i18n.results_found+'.</div>', 'ai'); 582 } else { 583 // AI returned error - capture and retry if attempts left 584 let newFailedQuery = sql_query || 585 (response.data && response.data.sql_query) || 586 (response.sql_query) || lastFailedSqlQuery; 587 if (newFailedQuery) { 588 lastFailedSqlQuery = newFailedQuery; 589 } 590 lastErrorMessage = (response.data && response.data.message) ? response.data.message : 591 (response.message) ? response.message : 'Query generation failed'; 592 593 console.log('[RETRY EXEC] Attempt', attempt, 'failed. New error:', lastErrorMessage); 594 595 if (attempt >= maxAttempts) { 596 // Log final failure after all retries exhausted 597 logExecutionResult(false, lastFailedSqlQuery, lastErrorMessage, attempt); 598 executionRetryCount = 0; // Reset 599 600 removeLoading(); 601 $('#process-query').prop('disabled', false); 602 clearErrorMessages(); 603 renderMessage('<div class="query-error">'+(askeet_query_assistant_i18n.ai_failed_5_times || askeet_query_assistant_i18n.ai_failed_3_times)+'</div>', 'ai', null, true); 604 } else { 605 setTimeout(tryProcessQueryWithContext, 1000); 606 } 607 } 608 }, 609 error: function(xhr, status, error) { 610 lastErrorMessage = error || 'Network error'; 611 console.log('[RETRY EXEC] Network error on attempt', attempt, ':', lastErrorMessage); 612 613 if (attempt >= maxAttempts) { 614 // Log final failure after all retries exhausted (network error) 615 logExecutionResult(false, lastFailedSqlQuery, 'Network error: ' + lastErrorMessage, attempt); 616 executionRetryCount = 0; // Reset 617 618 removeLoading(); 619 $('#process-query').prop('disabled', false); 620 clearErrorMessages(); 621 renderMessage('<div class="query-error">'+askeet_query_assistant_i18n.error_communication+'</div>', 'ai', null, true); 622 } else { 623 setTimeout(tryProcessQueryWithContext, 1000); 624 } 625 } 626 }); 627 } 628 629 tryProcessQueryWithContext(); 452 630 } 453 631 … … 525 703 526 704 if (response.success) { 705 // Log successful execution 706 logExecutionResult(true, currentSqlQuery, '', executionRetryCount); 707 executionRetryCount = 0; // Reset retry count after success 708 527 709 lastResults = response.data.results; 528 710 lastResultsHtml = response.data.html_results; … … 627 809 } 628 810 else { 629 clearErrorMessages(); 630 let errorMsg = ''; 631 if (response.data && response.data.message && response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1) { 632 errorMsg = response.data.message; 811 // SQL execution error - capture for potential retry 812 let executionError = (response.data && response.data.message) ? response.data.message : 'SQL execution failed'; 813 let failedSql = currentSqlQuery || ''; 814 815 console.log('[EXEC ERROR] SQL:', failedSql, 'Error:', executionError); 816 817 // Check if it's a non-retryable error (like security restriction) 818 let isNonRetryableError = response.data && response.data.message && 819 response.data.message.indexOf('Seules les requêtes SELECT sont autorisées') !== -1; 820 821 if (isNonRetryableError) { 822 // Security error - don't retry, just show error and log failure 823 logExecutionResult(false, failedSql, executionError, 0); 824 clearErrorMessages(); 825 renderMessage('<div class="query-error">'+response.data.message+'</div>', 'ai', null, true); 826 } else if (isNewQuery && lastQueryRequest) { 827 // This was a new query that failed on execution - trigger auto-retry with context 828 console.log('[EXEC ERROR] Triggering auto-retry with execution error context'); 829 executionRetryCount++; // Increment retry count 830 retryWithExecutionError(lastQueryRequest, failedSql, executionError); 633 831 } else { 634 errorMsg = askeet_query_assistant_i18n.ai_failed_3_times; 635 } 636 renderMessage('<div class="query-error">'+errorMsg+'</div>', 'ai', null, true); 832 // Not a new query or no original query - just show error with retry button and log failure 833 logExecutionResult(false, failedSql, executionError, executionRetryCount); 834 executionRetryCount = 0; // Reset 835 clearErrorMessages(); 836 renderMessage('<div class="query-error">'+executionError+'</div>', 'ai', null, true); 837 } 637 838 } 638 839 }, … … 640 841 removeLoading(); 641 842 clearErrorMessages(); 843 // Log network/AJAX error 844 logExecutionResult(false, currentSqlQuery, 'Network error: ' + (error || status), executionRetryCount); 845 executionRetryCount = 0; // Reset 846 642 847 try { 643 848 var resp = xhr.responseJSON || JSON.parse(xhr.responseText); … … 1441 1646 }); 1442 1647 1648 // ======================================== 1649 // EXECUTION RESULT LOGGING 1650 // ======================================== 1651 1652 /** 1653 * Log the actual SQL execution result to the API for tracking. 1654 * Updates the existing query record with WordPress execution results. 1655 * 1656 * @param {boolean} success - Whether the SQL executed successfully 1657 * @param {string} sqlQuery - The SQL that was executed 1658 * @param {string} error - Error message if execution failed 1659 * @param {number} retryCount - Number of retries that were needed 1660 */ 1661 function logExecutionResult(success, sqlQuery, error, retryCount) { 1662 // Don't block execution, fire and forget 1663 let requestData = { 1664 install_id: installId, 1665 sql_query: sqlQuery || '', 1666 execution_success: success, 1667 execution_error: error || '', 1668 retry_count: retryCount || 0 1669 }; 1670 1671 // Include query_id if available for more reliable matching 1672 if (currentQueryId) { 1673 requestData.query_id = currentQueryId; 1674 } 1675 1676 $.ajax({ 1677 url: apiUrl + '/log-execution-result', 1678 type: 'POST', 1679 contentType: 'application/json', 1680 data: JSON.stringify(requestData), 1681 success: function(response) { 1682 console.log('[EXEC LOG] Execution result logged:', success ? 'SUCCESS' : 'FAILED', 'query_id:', currentQueryId); 1683 }, 1684 error: function(xhr, status, err) { 1685 console.log('[EXEC LOG] Failed to log execution result:', err); 1686 } 1687 }); 1688 } 1689 1443 1690 // Star click - submit rating 1444 1691 $(document).on('click', '.wcqa-star', function() {
Note: See TracChangeset
for help on using the changeset viewer.