PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.8
MxChat – AI Chatbot & Content Generation for WordPress v3.2.8
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 | admin/class-pinecone-manager.php +51 -99 3.2.213.2.8 View file →
@@ -493,12 +493,8 @@
493 493 $namespace = $pinecone_options['mxchat_pinecone_namespace'] ?? '';
494 494
495 495 // Pinecone's list endpoint returns vector IDs with pagination
496 496 // We then fetch the metadata for those specific IDs
497 - // NOTE (plan 793b82): /vectors/list is a GET endpoint with query parameters.
498 - // A POST is answered 200-with-an-empty-body, which reads as "no vectors" —
499 - // this call used to POST and only ever "worked" because the empty result
500 - // tripped the query-based fallback below.
501 497 $list_url = "https://{$host}/vectors/list";
502 498
503 499 // Calculate pagination token from page number
504 500 // Pinecone uses cursor-based pagination, so we need to handle this differently
@@ -522,20 +518,19 @@
522 518 $list_params['paginationToken'] = $stored_tokens[$page];
523 519 }
524 520 }
525 521
526 - $response = wp_remote_get($list_url . '?' . http_build_query($list_params), array(
522 + $response = wp_remote_post($list_url, array(
527 523 'headers' => array(
528 524 'Api-Key' => $api_key,
529 - 'accept' => 'application/json'
525 + 'Content-Type' => 'application/json'
530 526 ),
527 + 'body' => json_encode($list_params),
531 528 'timeout' => 15
532 529 ));
533 530
534 531 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
535 - // Genuine transport/API failure — fall back to the query-based approach,
536 - // but say so: a silent fallback is what masked the POST bug for a year.
537 - error_log('MxChat Pinecone: /vectors/list GET failed (' . (is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_response_code($response)) . ') — falling back to query-based listing.');
532 + // Fallback to query-based approach
538 533 return $this->mxchat_query_based_list($pinecone_options, $page, $per_page, $bot_id, $content_type);
539 534 }
540 535
541 536 $body = wp_remote_retrieve_body($response);
@@ -554,26 +549,15 @@
554 549 $vector_ids[] = $vector['id'];
555 550 }
556 551 }
557 552
558 - // A 200 with no vector ids is a REAL answer now (the namespace is empty),
559 - // not the failure signature it was under POST — return the empty listing
560 - // rather than routing into the fallback and masking a broken primary.
553 + // If list endpoint returned empty, fall back to query-based approach
561 554 if (empty($vector_ids)) {
562 - return array('data' => array(), 'total' => 0);
555 + return $this->mxchat_query_based_list($pinecone_options, $page, $per_page, $bot_id, $content_type);
563 556 }
564 557
565 558 // Fetch metadata for these vector IDs
566 - $fetched = $this->mxchat_fetch_vectors_by_ids_for_list($pinecone_options, $vector_ids, $page, $per_page, $bot_id, $content_type);
567 -
568 - // null = the fetch itself failed in transport (distinct from "ids had no
569 - // metadata matches") — log and fall back so the screen still lists.
570 - if ($fetched === null) {
571 - error_log('MxChat Pinecone: /vectors/fetch GET failed for the listing — falling back to query-based listing.');
572 - return $this->mxchat_query_based_list($pinecone_options, $page, $per_page, $bot_id, $content_type);
573 - }
574 -
575 - return $fetched;
559 + return $this->mxchat_fetch_vectors_by_ids_for_list($pinecone_options, $vector_ids, $page, $per_page, $bot_id, $content_type);
576 560 }
577 561
578 562 /**
579 563 * Query-based listing fallback when list endpoint fails
@@ -677,31 +661,29 @@
677 661 $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
678 662 $host = $pinecone_options['mxchat_pinecone_host'] ?? '';
679 663 $namespace = $pinecone_options['mxchat_pinecone_namespace'] ?? '';
680 664
681 - // NOTE (plan 793b82): /vectors/fetch is a GET endpoint, and Pinecone expects
682 - // the ids repeated (ids=a&ids=b) — http_build_query would emit ids[0]=a.
683 - // A POST here is answered 200-with-an-empty-body, which reads as "no vectors".
684 - $fetch_query = array();
685 - foreach ($vector_ids as $fetch_vid) {
686 - $fetch_query[] = 'ids=' . rawurlencode($fetch_vid);
687 - }
665 + $fetch_url = "https://{$host}/vectors/fetch";
666 +
667 + $fetch_data = array(
668 + 'ids' => $vector_ids
669 + );
670 +
688 671 if (!empty($namespace)) {
689 - $fetch_query[] = 'namespace=' . rawurlencode($namespace);
672 + $fetch_data['namespace'] = $namespace;
690 673 }
691 674
692 - $response = wp_remote_get("https://{$host}/vectors/fetch?" . implode('&', $fetch_query), array(
675 + $response = wp_remote_post($fetch_url, array(
693 676 'headers' => array(
694 677 'Api-Key' => $api_key,
695 - 'accept' => 'application/json'
678 + 'Content-Type' => 'application/json'
696 679 ),
680 + 'body' => json_encode($fetch_data),
697 681 'timeout' => 15
698 682 ));
699 683
700 - // Transport/API failure is distinct from "nothing matched": return null so
701 - // the caller can tell the difference (plan 793b82's empty-vs-failed rule).
702 684 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
703 - return null;
685 + return array('data' => array(), 'total' => 0);
704 686 }
705 687
706 688 $body = wp_remote_retrieve_body($response);
707 689 $data = json_decode($body, true);
@@ -1122,45 +1104,31 @@
1122 1104 if (empty($vector_id_map)) {
1123 1105 return array();
1124 1106 }
1125 1107
1126 - // Batch check Pinecone using fetch API.
1127 - // NOTE (plan 793b82): /vectors/fetch is a GET endpoint with the ids
1128 - // repeated in the query string (ids=a&ids=b); the old POST here was
1129 - // answered 200-with-an-empty-body, so this scan saw NOTHING as indexed
1130 - // and callers re-embedded content Pinecone already had. Chunk size 100:
1131 - // measured on a live serverless index, ~9KB of URL is accepted and
1132 - // ~18KB draws HTTP 414, so 100 32-char ids (~4KB) leaves real margin.
1133 - $namespace = $pinecone_options['mxchat_pinecone_namespace'] ?? '';
1108 + // Batch check Pinecone using fetch API (max 1000 IDs per request)
1134 1109 $all_vector_ids = array_keys($vector_id_map);
1135 - $chunks = array_chunk($all_vector_ids, 100);
1110 + $chunks = array_chunk($all_vector_ids, 1000);
1136 1111 $processed_data = array();
1137 1112
1138 1113 foreach ($chunks as $chunk) {
1139 - $fetch_query = array();
1140 - foreach ($chunk as $fetch_vid) {
1141 - $fetch_query[] = 'ids=' . rawurlencode($fetch_vid);
1142 - }
1143 - if (!empty($namespace)) {
1144 - $fetch_query[] = 'namespace=' . rawurlencode($namespace);
1145 - }
1114 + $fetch_url = "https://{$host}/vectors/fetch";
1146 1115
1147 - $response = wp_remote_get("https://{$host}/vectors/fetch?" . implode('&', $fetch_query), array(
1116 + $response = wp_remote_post($fetch_url, array(
1148 1117 'headers' => array(
1149 1118 'Api-Key' => $api_key,
1150 - 'accept' => 'application/json'
1119 + 'Content-Type' => 'application/json'
1151 1120 ),
1121 + 'body' => json_encode(array('ids' => $chunk)),
1152 1122 'timeout' => 30
1153 1123 ));
1154 1124
1155 1125 if (is_wp_error($response)) {
1156 - error_log('MxChat Pinecone: processed-content scan /vectors/fetch GET failed: ' . $response->get_error_message());
1157 1126 continue;
1158 1127 }
1159 1128
1160 1129 $response_code = wp_remote_retrieve_response_code($response);
1161 1130 if ($response_code !== 200) {
1162 - error_log('MxChat Pinecone: processed-content scan /vectors/fetch GET returned HTTP ' . $response_code);
1163 1131 continue;
1164 1132 }
1165 1133
1166 1134 $body = wp_remote_retrieve_body($response);
@@ -1343,9 +1311,8 @@
1343 1311 */
1344 1312 public function fetch_pinecone_vectors_by_ids($pinecone_options, $vector_ids) {
1345 1313 $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
1346 1314 $host = $pinecone_options['mxchat_pinecone_host'] ?? '';
1347 - $namespace = $pinecone_options['mxchat_pinecone_namespace'] ?? '';
1348 1315
1349 1316 if (empty($api_key) || empty($host) || empty($vector_ids)) {
1350 1317 return array();
1351 1318 }
@@ -1350,53 +1317,44 @@
1350 1317 return array();
1351 1318 }
1352 1319
1353 1320 try {
1354 - // NOTE (plan 793b82): /vectors/fetch is a GET endpoint with the ids
1355 - // repeated in the query string; the old POST here was answered
1356 - // 200-with-an-empty-body. Chunked at 100 ids to stay well under the
1357 - // measured HTTP 414 URL-length boundary.
1358 - $vectors = array();
1359 - foreach (array_chunk(array_values($vector_ids), 100) as $chunk) {
1360 - $fetch_query = array();
1361 - foreach ($chunk as $fetch_vid) {
1362 - $fetch_query[] = 'ids=' . rawurlencode($fetch_vid);
1363 - }
1364 - if (!empty($namespace)) {
1365 - $fetch_query[] = 'namespace=' . rawurlencode($namespace);
1366 - }
1321 + $fetch_url = "https://{$host}/vectors/fetch";
1367 1322
1368 - $response = wp_remote_get("https://{$host}/vectors/fetch?" . implode('&', $fetch_query), array(
1369 - 'headers' => array(
1370 - 'Api-Key' => $api_key,
1371 - 'accept' => 'application/json'
1372 - ),
1373 - 'timeout' => 30
1374 - ));
1323 + // Pinecone fetch API allows fetching specific vectors by ID
1324 + $fetch_data = array(
1325 + 'ids' => array_values($vector_ids)
1326 + );
1375 1327
1376 - if (is_wp_error($response)) {
1377 - error_log('MxChat Pinecone: fetch_pinecone_vectors_by_ids GET failed: ' . $response->get_error_message());
1378 - continue;
1379 - }
1328 + $response = wp_remote_post($fetch_url, array(
1329 + 'headers' => array(
1330 + 'Api-Key' => $api_key,
1331 + 'Content-Type' => 'application/json'
1332 + ),
1333 + 'body' => json_encode($fetch_data),
1334 + 'timeout' => 30
1335 + ));
1380 1336
1381 - if (wp_remote_retrieve_response_code($response) !== 200) {
1382 - error_log('MxChat Pinecone: fetch_pinecone_vectors_by_ids GET returned HTTP ' . wp_remote_retrieve_response_code($response));
1383 - continue;
1384 - }
1337 + if (is_wp_error($response)) {
1338 + return array();
1339 + }
1385 1340
1386 - $data = json_decode(wp_remote_retrieve_body($response), true);
1387 - if (isset($data['vectors']) && is_array($data['vectors'])) {
1388 - $vectors += $data['vectors'];
1389 - }
1341 + $response_code = wp_remote_retrieve_response_code($response);
1342 +
1343 + if ($response_code !== 200) {
1344 + return array();
1390 1345 }
1391 1346
1392 - if (empty($vectors)) {
1347 + $body = wp_remote_retrieve_body($response);
1348 + $data = json_decode($body, true);
1349 +
1350 + if (!isset($data['vectors'])) {
1393 1351 return array();
1394 1352 }
1395 1353
1396 1354 $processed_data = array();
1397 1355
1398 - foreach ($vectors as $vector_id => $vector_data) {
1356 + foreach ($data['vectors'] as $vector_id => $vector_data) {
1399 1357 $metadata = $vector_data['metadata'] ?? array();
1400 1358 $source_url = $metadata['source_url'] ?? '';
1401 1359
1402 1360 if (!empty($source_url)) {
@@ -1439,9 +1397,8 @@
1439 1397 */
1440 1398 public function mxchat_delete_all_from_pinecone($pinecone_options, $content_type_filter = '') {
1441 1399 $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
1442 1400 $host = $pinecone_options['mxchat_pinecone_host'] ?? '';
1443 - $namespace = $pinecone_options['mxchat_pinecone_namespace'] ?? '';
1444 1401
1445 1402 if (empty($api_key) || empty($host)) {
1446 1403 return array(
1447 1404 'success' => false,
@@ -1486,9 +1443,9 @@
1486 1443 $batch_size = 100;
1487 1444 $batches = array_chunk($vector_ids, $batch_size);
1488 1445
1489 1446 foreach ($batches as $batch) {
1490 - $result = $this->mxchat_delete_pinecone_batch($batch, $api_key, $host, $namespace);
1447 + $result = $this->mxchat_delete_pinecone_batch($batch, $api_key, $host);
1491 1448 if ($result['success']) {
1492 1449 $total_deleted += count($batch);
1493 1450 } else {
1494 1451 $failed_batches++;
@@ -1529,9 +1486,9 @@
1529 1486
1530 1487 /**
1531 1488 * Deletes batch of vectors from Pinecone database
1532 1489 */
1533 - public function mxchat_delete_pinecone_batch($vector_ids, $api_key, $host, $namespace = '') {
1490 + public function mxchat_delete_pinecone_batch($vector_ids, $api_key, $host) {
1534 1491 // Build the API endpoint
1535 1492 $api_endpoint = "https://{$host}/vectors/delete";
1536 1493
1537 1494 // Prepare the request body with the IDs
@@ -1537,13 +1494,8 @@
1537 1494 // Prepare the request body with the IDs
1538 1495 $request_body = array(
1539 1496 'ids' => $vector_ids
1540 1497 );
1541 -
1542 - // Add namespace if provided — omitting it targets the default namespace
1543 - if (!empty($namespace)) {
1544 - $request_body['namespace'] = $namespace;
1545 - }
1546 1498
1547 1499 // Make the deletion request
1548 1500 $response = wp_remote_post($api_endpoint, array(
1549 1501 'headers' => array(