PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.23
MxChat – AI Chatbot & Content Generation for WordPress v3.2.23
3.2.25 3.2.24 3.2.23 3.2.22 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 All 156 releases
← All changes | admin/class-pinecone-documents.php +36 -140 3.2.25 → 3.2.23 View file →
@@ -735,33 +735,24 @@
735 735 * the first run and full is the whole joined run, both un-normalised.
736 736 */
737 737 private static function code_runs($text) {
738 738 $runs = array();
739 - $text = (string) $text;
740 739 $pattern = '/(?<![A-Za-z0-9])([A-Za-z0-9]+(?:[.\-][A-Za-z0-9]+)*)((?:[ .\-]\d{2,6}(?![A-Za-z0-9])){1,3})?(?![A-Za-z0-9])/u';
741 - $offset = 0;
742 - $length = strlen($text);
743 - // 0d7bd4: walked one match at a time. Only a head that IS a code keeps
744 - // its trailing digit groups; a plain word ("part 4471-AB-12") used to
745 - // take "4471" as its tail and the digit-led number was never stored.
746 - while ($offset < $length && preg_match($pattern, $text, $match, PREG_OFFSET_CAPTURE, $offset)) {
747 - $head = rtrim($match[1][0], '.');
748 - $head_end = $match[1][1] + strlen($match[1][0]);
749 - $is_code = strlen($head) >= self::CODE_MIN_LEN && preg_match('/\d/', $head);
750 - if ($is_code) {
751 - $has_letter = (bool) preg_match('/[A-Za-z]/', $head);
752 - $separators = preg_match_all('/[.\-]/', $head);
753 - if (!$has_letter && $separators < 2) {
754 - $is_code = false; // 2024, 26.5, 10-20
755 - }
740 + if (!preg_match_all($pattern, (string) $text, $m, PREG_SET_ORDER)) {
741 + return $runs;
742 + }
743 + foreach ($m as $match) {
744 + $head = rtrim($match[1], '.');
745 + if (strlen($head) < self::CODE_MIN_LEN || !preg_match('/\d/', $head)) {
746 + continue;
756 747 }
757 - if (!$is_code) {
758 - $offset = $head_end;
759 - continue;
748 + $has_letter = (bool) preg_match('/[A-Za-z]/', $head);
749 + $separators = preg_match_all('/[.\-]/', $head);
750 + if (!$has_letter && $separators < 2) {
751 + continue; // 2024, 26.5, 10-20
760 752 }
761 - $tail = isset($match[2]) && $match[2][1] >= 0 ? $match[2][0] : '';
753 + $tail = isset($match[2]) ? $match[2] : '';
762 754 $runs[] = array($head, $head . $tail);
763 - $offset = $match[0][1] + strlen($match[0][0]);
764 755 }
765 756 return $runs;
766 757 }
767 758
@@ -789,11 +780,9 @@
789 780 if (count($codes) >= self::CODES_MAX) {
790 781 break;
791 782 }
792 783 }
793 - // 0d7bd4: strval — PHP turns an all-digit array key into an int, and
794 - // Pinecone refuses a list that mixes strings and numbers.
795 - return array_slice(array_map('strval', array_keys($codes)), 0, self::CODES_MAX);
784 + return array_slice(array_keys($codes), 0, self::CODES_MAX);
796 785 }
797 786
798 787 /**
799 788 * What a question's tokens expand to for the codes $in filter: the
@@ -820,9 +809,9 @@
820 809 if (count($codes) >= 200) {
821 810 break;
822 811 }
823 812 }
824 - return array_slice(array_map('strval', array_keys($codes)), 0, 200);
813 + return array_slice(array_keys($codes), 0, 200);
825 814 }
826 815
827 816 /**
828 817 * Order keyword-leg hits so an exact code match outranks a prefix-only
@@ -1100,55 +1089,29 @@
1100 1089 }
1101 1090
1102 1091 /** Dimension of the active embedding model (same table the KB screen uses). */
1103 1092 public static function expected_dimension() {
1104 - $dimension = self::expected_dimension_checked();
1105 - return is_wp_error($dimension) ? 0 : (int) $dimension;
1106 - }
1107 -
1108 - /**
1109 - * 418afd: the dimension the site's embeddings really have, or why it is
1110 - * not known. A Custom Provider embedding model is asked for its size
1111 - * (MxChat_Utils::expected_embedding_dimension()); the standard dropdown is
1112 - * disabled on such a site and used to size the index anyway. The filter
1113 - * still has the last word, and can supply a number when the model could
1114 - * not be asked.
1115 - *
1116 - * @param bool $force Ask a custom model again even after a recent failure (the card's buttons).
1117 - * @return int|WP_Error
1118 - */
1119 - public static function expected_dimension_checked($force = false) {
1120 1093 $options = get_option('mxchat_options', array());
1121 - $options = is_array($options) ? $options : array();
1122 - $custom = isset($options['custom_provider_for_embeddings']) && $options['custom_provider_for_embeddings'] === 'on';
1123 - $selected_model = $custom
1124 - ? MxChat_Utils::get_selected_embedding_model($options)
1125 - : ($options['embedding_model'] ?? 'text-embedding-ada-002');
1126 - $dimension = MxChat_Utils::expected_embedding_dimension($options, $force);
1127 - if (is_wp_error($dimension)) {
1128 - $supplied = (int) apply_filters('mxchat_pinecone_expected_dimension', 0, $selected_model);
1129 - return $supplied > 0 ? $supplied : $dimension;
1094 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
1095 + $model_dimensions = array(
1096 + 'text-embedding-ada-002' => 1536,
1097 + 'text-embedding-3-small' => 1536,
1098 + 'text-embedding-3-large' => 3072,
1099 + 'voyage-2' => 1024,
1100 + 'voyage-large-2' => 1536,
1101 + 'voyage-3-large' => 2048,
1102 + 'gemini-embedding-001' => 1536,
1103 + );
1104 + if (strpos($selected_model, 'voyage-3-large') === 0) {
1105 + return intval($options['voyage_output_dimension'] ?? 2048);
1130 1106 }
1131 - if (!$custom && (strpos($selected_model, 'voyage-3-large') === 0 || strpos($selected_model, 'gemini-embedding') === 0)) {
1132 - return (int) $dimension; // adjustable-size models: the owner's own setting, never filtered (as before)
1107 + if (strpos($selected_model, 'gemini-embedding') === 0) {
1108 + return intval($options['gemini_output_dimension'] ?? 1536);
1133 1109 }
1134 - return (int) apply_filters('mxchat_pinecone_expected_dimension', $dimension, $selected_model);
1110 + $dimension = apply_filters('mxchat_pinecone_expected_dimension', $model_dimensions[$selected_model] ?? 1536, $selected_model);
1111 + return (int) $dimension;
1135 1112 }
1136 1113
1137 - /** Plain-language refusal for the card when a custom model's size is unknown. (Plain __(): the card's JS text-escapes it.) */
1138 - public static function dimension_unknown_message(WP_Error $error, $action) {
1139 - $reason = trim($error->get_error_message());
1140 - $tail = $action === 'create'
1141 - ? __('The index was not created.', 'mxchat')
1142 - : __('The index was not checked.', 'mxchat');
1143 - return sprintf(
1144 - /* translators: 1: the error the embedding endpoint returned, 2: what did not happen */
1145 - __('Could not determine the dimension of your Custom Provider embedding model (%1$s). %2$s Check the Custom Provider embedding settings, then try again.', 'mxchat'),
1146 - $reason !== '' ? $reason : __('no answer from the embedding endpoint', 'mxchat'),
1147 - $tail
1148 - );
1149 - }
1150 -
1151 1114 // =====================================================================
1152 1115 // Options (raw writes — the registered sanitizer rebuilds the array and
1153 1116 // would flip mxchat_use_pinecone on any isset key; the autosave handler
1154 1117 // writes this option the same raw way for the same reason)
@@ -1534,13 +1497,11 @@
1534 1497 } else {
1535 1498 $total = 0;
1536 1499 }
1537 1500 $per_namespace[self::ns_key($namespace)] = self::new_namespace_entry($namespace, $total, 'running');
1538 - // 47c61f: in the copy order (default first, then by name). Pinecone's
1539 - // stats object lists namespaces in a different order call to call.
1540 - foreach (self::namespace_queue($ns_counts) as $ns_name) {
1541 - if ((string) $ns_name !== (string) $namespace) {
1542 - $other_namespaces[self::ns_key($ns_name)] = (int) $ns_counts[$ns_name];
1501 + foreach ($ns_counts as $ns_name => $ns_count) {
1502 + if ($ns_name !== $namespace && $ns_count > 0) {
1503 + $other_namespaces[self::ns_key($ns_name)] = $ns_count;
1543 1504 }
1544 1505 }
1545 1506 }
1546 1507 $source_dimension = (int) ($stats['dimension'] ?? 0);
@@ -1794,48 +1755,8 @@
1794 1755 implode(', ', $parts)
1795 1756 );
1796 1757 }
1797 1758
1798 - /**
1799 - * 5872ef: bots that still keep the given Pinecone host as their own (the
1800 - * Multi-Bot add-on stores a host per bot). Core knows nothing about the
1801 - * bots table — the add-on answers the filter, and with it inactive the
1802 - * list is empty. Delete old index refuses while this is not empty.
1803 - *
1804 - * @return array bot id → bot name
1805 - */
1806 - public static function bots_on_host($host) {
1807 - $host = strtolower(trim(str_replace(array('https://', 'http://'), '', (string) $host), '/'));
1808 - if ($host === '') {
1809 - return array();
1810 - }
1811 - $bots = apply_filters('mxchat_pinecone_bots_on_host', array(), $host);
1812 - if (!is_array($bots)) {
1813 - return array();
1814 - }
1815 - $named = array();
1816 - foreach ($bots as $bot_id => $name) {
1817 - $name = is_scalar($name) ? trim((string) $name) : '';
1818 - $named[(string) $bot_id] = $name !== '' ? $name : (string) $bot_id;
1819 - }
1820 - return $named;
1821 - }
1822 -
1823 - /** Plain-language refusal naming every bot still on the old index. (Plain __(): the card's JS text-escapes it.) */
1824 - public static function bots_on_host_message(array $bots) {
1825 - return sprintf(
1826 - /* translators: 1: number of bots, 2: comma-separated bot names */
1827 - _n(
1828 - '%1$s bot still uses the old index: %2$s. Move it to the new index first (Multi-Bot, All Bots, Move these bots to the new index), or it will stop answering. The old index stays.',
1829 - '%1$s bots still use the old index: %2$s. Move them to the new index first (Multi-Bot, All Bots, Move these bots to the new index), or they will stop answering. The old index stays.',
1830 - count($bots),
1831 - 'mxchat'
1832 - ),
1833 - number_format_i18n(count($bots)),
1834 - implode(', ', $bots)
1835 - );
1836 - }
1837 -
1838 1759 /** Record a step failure without ending the copy: status stays running, the cursor stays put. */
1839 1760 private static function migration_fail(array $state, $message) {
1840 1761 $state['error'] = (string) $message;
1841 1762 $state['error_at'] = time();
@@ -1921,13 +1842,9 @@
1921 1842 }
1922 1843 if ($name === '') {
1923 1844 wp_send_json_error(array('message' => esc_html__('Enter an index name first — the new document index will be created under that name.', 'mxchat')));
1924 1845 }
1925 - $dimension = self::expected_dimension_checked(true);
1926 - if (is_wp_error($dimension)) {
1927 - // 418afd: never size an index from a guess
1928 - wp_send_json_error(array('message' => self::dimension_unknown_message($dimension, 'create'), 'code' => 'dimension_unknown'));
1929 - }
1846 + $dimension = self::expected_dimension();
1930 1847 $index = self::create_document_index($name, $dimension, $cloud, $region, $language, $api_key);
1931 1848 if (is_wp_error($index)) {
1932 1849 $data = $index->get_error_data();
1933 1850 wp_send_json_error(array(
@@ -1990,13 +1907,9 @@
1990 1907 $host = trim(str_replace(array('https://', 'http://'), '', $host), '/');
1991 1908 if ($api_key === '' || $host === '') {
1992 1909 wp_send_json_error(array('message' => esc_html__('Enter the Pinecone API key and host first.', 'mxchat')));
1993 1910 }
1994 - $dimension = self::expected_dimension_checked(true);
1995 - if (is_wp_error($dimension)) {
1996 - wp_send_json_error(array('ok' => false, 'message' => self::dimension_unknown_message($dimension, 'check'), 'code' => 'dimension_unknown'));
1997 - }
1998 - $check = self::check_document_index($host, $api_key, $dimension);
1911 + $check = self::check_document_index($host, $api_key, self::expected_dimension());
1999 1912 if (!$check['ok']) {
2000 1913 wp_send_json_error($check);
2001 1914 }
2002 1915 $options = get_option(self::OPTION, array());
@@ -2046,13 +1959,9 @@
2046 1959 $target_host = self::posted_target_host($options);
2047 1960 if ($target_host === '') {
2048 1961 wp_send_json_error(array('message' => esc_html__('Enter the host of the document index to copy into (or create one with Create index for me).', 'mxchat')));
2049 1962 }
2050 - $dimension = self::expected_dimension_checked(true);
2051 - if (is_wp_error($dimension)) {
2052 - wp_send_json_error(array('message' => self::dimension_unknown_message($dimension, 'check'), 'code' => 'dimension_unknown'));
2053 - }
2054 - $check = self::check_document_index($target_host, $api_key, $dimension);
1963 + $check = self::check_document_index($target_host, $api_key, self::expected_dimension());
2055 1964 if (!$check['ok']) {
2056 1965 wp_send_json_error(array('message' => $check['message']));
2057 1966 }
2058 1967 $target = array('api_key' => $api_key, 'host' => strtolower($check['host']), 'namespace' => $namespace, 'index_type' => 'document');
@@ -2113,21 +2022,8 @@
2113 2022 // finished copy whose count matches — not just the one copied last.
2114 2023 // Before this, a Multi-Bot or legacy-namespace site could delete
2115 2024 // records that were never copied.
2116 2025 $missing = self::uncopied_namespaces($state, $target['api_key']);
2117 - // 5872ef: a bot that still keeps the old index as its own host stops
2118 - // answering the moment that index is deleted, however complete the
2119 - // copy is. Refuse and name the bots; when namespaces are uncopied too,
2120 - // say both in one answer.
2121 - $bots = self::bots_on_host((string) ($state['source_host'] ?? ''));
2122 - if (!empty($bots)) {
2123 - $refusal = array('message' => self::bots_on_host_message($bots), 'bots' => $bots);
2124 - if (is_array($missing) && !empty($missing)) {
2125 - $refusal['message'] .= ' ' . self::uncopied_message($missing);
2126 - $refusal['uncopied'] = $missing;
2127 - }
2128 - wp_send_json_error($refusal);
2129 - }
2130 2026 if (is_wp_error($missing)) {
2131 2027 wp_send_json_error(array('message' => sprintf(__('Could not read the old index before deleting it (%s). The old index stays.', 'mxchat'), $missing->get_error_message())));
2132 2028 }
2133 2029 if (!empty($missing)) {