From 83e42150a97ce4f1a610db2357c77fa7b765d947 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:31:03 +0530 Subject: [PATCH 01/22] run an ajax for preview --- .../Classifai/Providers/OpenAI/Embeddings.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index 84b22f5e7..27052b26a 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -84,6 +84,7 @@ public function register() { add_filter( 'rest_api_init', [ $this, 'add_process_content_meta_to_rest_api' ] ); add_action( 'add_meta_boxes', [ $this, 'add_metabox' ] ); add_action( 'save_post', [ $this, 'save_metabox' ] ); + add_action( 'wp_ajax_get_post_classifier_embeddings_preview_data', array( $this, 'get_post_classifier_embeddings_preview_data' ) ); } } @@ -392,6 +393,27 @@ public function supported_taxonomies() { return apply_filters( 'classifai_openai_embeddings_taxonomies', $this->get_supported_taxonomies() ); } + /** + * Get the list of post types for settings. + * + * @since 2.6.0 + * + * @return array + */ + public function get_post_classifier_embeddings_preview_data() { + $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false; + + if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-openai_embeddings-action' ) ) { + wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) ); + } + + $post_id = filter_input( INPUT_POST, 'post_id', FILTER_SANITIZE_NUMBER_INT ); + + $embeddings_terms = $this->generate_embeddings_for_post( $post_id, true ); + + return wp_send_json_success( $embeddings_terms ); + } + /** * Trigger embedding generation for content being saved. * From 390037f2aec0327d85e2798f4d933c56b44f832d Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:32:36 +0530 Subject: [PATCH 02/22] init dry run for embedding terms --- .../Classifai/Providers/OpenAI/Embeddings.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index 27052b26a..6f4c40e50 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -417,9 +417,12 @@ public function get_post_classifier_embeddings_preview_data() { /** * Trigger embedding generation for content being saved. * - * @param int $post_id ID of post being saved. + * @param int $post_id ID of post being saved. + * @param bool $dryrun Whether to run the process or just return the data. + * + * @return array|WP_Error */ - public function generate_embeddings_for_post( $post_id ) { + public function generate_embeddings_for_post( $post_id, $dryrun = false ) { // Don't run on autosaves. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; @@ -434,14 +437,17 @@ public function generate_embeddings_for_post( $post_id ) { // Only run on supported post types and statuses. if ( - ! in_array( $post->post_type, $this->supported_post_types(), true ) || - ! in_array( $post->post_status, $this->supported_post_statuses(), true ) + ! $dryrun + && ( + ! in_array( $post->post_type, $this->supported_post_types(), true ) || + ! in_array( $post->post_status, $this->supported_post_statuses(), true ) + ) ) { return; } // Don't run if turned off for this particular post. - if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) ) { + if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) && ! $dryrun ) { return; } @@ -449,8 +455,11 @@ public function generate_embeddings_for_post( $post_id ) { // Add terms to this item based on embedding data. if ( $embeddings && ! is_wp_error( $embeddings ) ) { - update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); - $this->set_terms( $post_id, $embeddings ); + if ( ! $dryrun ) { + update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); + } + + return $this->set_terms( $post_id, $embeddings, $dryrun ); } } @@ -459,8 +468,11 @@ public function generate_embeddings_for_post( $post_id ) { * * @param int $post_id ID of post to set terms on. * @param array $embedding Embedding data. + * @param bool $dryrun Whether to run the process or just return the data. + * + * @return array|WP_Error */ - private function set_terms( int $post_id = 0, array $embedding = [] ) { + private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = false ) { if ( ! $post_id || ! get_post( $post_id ) ) { return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to set terms.', 'classifai' ) ); } From d07faa8d72e23d8810212995004c9da7a3826013 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:50:35 +0530 Subject: [PATCH 03/22] return terms instead of saving --- .../Classifai/Providers/OpenAI/Embeddings.php | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index 6f4c40e50..b21077f48 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -524,16 +524,55 @@ private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = f } // Set terms based on similarity. + $index = 0; + $result = []; + foreach ( $embedding_similarity as $tax => $terms ) { + // Get the taxonomy name. + $taxonomy = get_taxonomy( $tax ); + $tax_name = $taxonomy->labels->singular_name; + // Sort embeddings from lowest to highest. asort( $terms ); + // Return the terms if this is a dry run. + if ( $dryrun ) { + $result[ $index ] = new \stdClass(); + + $result[ $index ]->{$tax_name} = []; + + $term_added = 0; + foreach ( $terms as $term_id => $similarity ) { + // Convert $similarity to percentage. + $similarity = round( ( 1 - $similarity ), 2 ); + + // Stop if we have added the number of terms specified in settings. + if ( $number_to_add <= $term_added ) { + break; + } + + $result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found + 'label' => get_term( $term_id )->name, + 'score' => $similarity, + ]; + $term_added++; + } + } + // Only add the number of terms specified in settings. if ( count( $terms ) > $number_to_add ) { $terms = array_slice( $terms, 0, $number_to_add, true ); } - wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); + if ( ! $dryrun ) { + wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); + } + $index++; + } + + // Return if its a dry run. + if ( $dryrun ) { + return $result; } } From 3a128ae11b3e8e8201d04a18b2ee1487d4ed2a15 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:51:59 +0530 Subject: [PATCH 04/22] display the preivew snippet on the tab --- includes/Classifai/Services/Service.php | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php index b5d9cd331..0be1a6dfb 100644 --- a/includes/Classifai/Services/Service.php +++ b/includes/Classifai/Services/Service.php @@ -152,7 +152,10 @@ public function render_settings_page() { // Find the right provider class. $provider = find_provider_class( $this->provider_classes ?? [], 'Natural Language Understanding' ); - if ( ! is_wp_error( $provider ) && ! empty( $provider->can_register() ) && $provider->is_feature_enabled( 'content_classification' ) ) : + if ( + ! is_wp_error( $provider ) && ! empty( $provider->can_register() ) + && ( 'openai_embeddings' === $active_tab || $provider->is_feature_enabled( 'content_classification' ) ) + ) : ?>
- +

- +
- $feature ) : ?> -
-
-
- -
+ $feature ) : + ?> +
+
+
+ +
From 2b6e488ae607bd445694cc132a5d4f8275f1a4f3 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:52:18 +0530 Subject: [PATCH 05/22] check for nonce --- includes/Classifai/Admin/PreviewClassifierData.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/includes/Classifai/Admin/PreviewClassifierData.php b/includes/Classifai/Admin/PreviewClassifierData.php index d5cfa0973..ef2984ca6 100644 --- a/includes/Classifai/Admin/PreviewClassifierData.php +++ b/includes/Classifai/Admin/PreviewClassifierData.php @@ -20,7 +20,7 @@ public function __construct() { public function get_post_classifier_preview_data() { $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false; - if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-action' ) ) { + if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-watson_nlu-action' ) ) { wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) ); } @@ -44,7 +44,13 @@ public function get_post_classifier_preview_data() { public function get_post_search_results() { $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false; - if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-action' ) ) { + if ( + ! $nonce + || ( + ! wp_verify_nonce( $nonce, 'classifai-previewer-openai_embeddings-action' ) + && ! wp_verify_nonce( $nonce, 'classifai-previewer-watson_nlu-nonce' ) + ) + ) { wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) ); } From cd1862d04cf610d6d321a994ca222c543d406b88 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Fri, 24 Nov 2023 21:52:34 +0530 Subject: [PATCH 06/22] handle the client side actions --- src/js/language-processing.js | 345 +++++++++++++++++++++------------- 1 file changed, 217 insertions(+), 128 deletions(-) diff --git a/src/js/language-processing.js b/src/js/language-processing.js index 3ea8dd16a..14460f443 100644 --- a/src/js/language-processing.js +++ b/src/js/language-processing.js @@ -2,121 +2,130 @@ import Choices from 'choices.js'; import '../scss/language-processing.scss'; ( () => { - const nonceElement = document.getElementById( 'classifai-previewer-nonce' ); - if ( ! nonceElement ) { - return; - } - /** Previewer nonce. */ - const previewerNonce = nonceElement.value; - - /** Feature statuses. */ - const featureStatuses = { - categoriesStatus: document.getElementById( - 'classifai-settings-category' - ).checked, - keywordsStatus: document.getElementById( 'classifai-settings-keyword' ) - .checked, - entitiesStatus: document.getElementById( 'classifai-settings-entity' ) - .checked, - conceptsStatus: document.getElementById( 'classifai-settings-concept' ) - .checked, - }; + let featureStatuses = {}; - const plurals = { - category: 'categories', - keyword: 'keywords', - entity: 'entities', - concept: 'concepts', - }; + const previewWatson = () => { + const nonceElement = document.getElementById( 'classifai-previewer-watson_nlu-nonce' ); + if ( ! nonceElement ) { + return; + } - document - .querySelectorAll( - '#classifai-settings-category, #classifai-settings-keyword, #classifai-settings-entity, #classifai-settings-concept' - ) - .forEach( ( item ) => { - item.addEventListener( 'change', ( e ) => { - if ( 'classifai-settings-category' === e.target.id ) { - featureStatuses.categoriesStatus = e.target.checked; - } + const getClassifierDataBtn = document.getElementById( + 'get-classifier-preview-data-btn' + ); + getClassifierDataBtn.addEventListener( 'click', showPreviewWatson ); + + /** Previewer nonce. */ + const previewerNonce = nonceElement.value; + + /** Feature statuses. */ + featureStatuses = { + categoriesStatus: document.getElementById( + 'classifai-settings-category' + ).checked, + keywordsStatus: document.getElementById( 'classifai-settings-keyword' ) + .checked, + entitiesStatus: document.getElementById( 'classifai-settings-entity' ) + .checked, + conceptsStatus: document.getElementById( 'classifai-settings-concept' ) + .checked, + }; - if ( 'classifai-settings-keyword' === e.target.id ) { - featureStatuses.keywordsStatus = e.target.checked; - } + const plurals = { + category: 'categories', + keyword: 'keywords', + entity: 'entities', + concept: 'concepts', + }; - if ( 'classifai-settings-entity' === e.target.id ) { - featureStatuses.entitiesStatus = e.target.checked; - } + document + .querySelectorAll( + '#classifai-settings-category, #classifai-settings-keyword, #classifai-settings-entity, #classifai-settings-concept' + ) + .forEach( ( item ) => { + item.addEventListener( 'change', ( e ) => { + if ( 'classifai-settings-category' === e.target.id ) { + featureStatuses.categoriesStatus = e.target.checked; + } - if ( 'classifai-settings-concept' === e.target.id ) { - featureStatuses.conceptsStatus = e.target.checked; - } + if ( 'classifai-settings-keyword' === e.target.id ) { + featureStatuses.keywordsStatus = e.target.checked; + } - const taxType = e.target.id.split( '-' ).at( -1 ); + if ( 'classifai-settings-entity' === e.target.id ) { + featureStatuses.entitiesStatus = e.target.checked; + } - if ( e.target.checked ) { - document - .querySelector( `.tax-row--${ plurals[ taxType ] }` ) - .classList.remove( 'tax-row--hide' ); - } else { - document - .querySelector( `.tax-row--${ plurals[ taxType ] }` ) - .classList.add( 'tax-row--hide' ); - } + if ( 'classifai-settings-concept' === e.target.id ) { + featureStatuses.conceptsStatus = e.target.checked; + } + + const taxType = e.target.id.split( '-' ).at( -1 ); + + if ( e.target.checked ) { + document + .querySelector( `.tax-row--${ plurals[ taxType ] }` ) + .classList.remove( 'tax-row--hide' ); + } else { + document + .querySelector( `.tax-row--${ plurals[ taxType ] }` ) + .classList.add( 'tax-row--hide' ); + } + } ); } ); - } ); - /** - * Live preview features. - * - * @param {Object} e The event object. - */ - function showPreview( e ) { - /** Category thresholds. */ - const categoryThreshold = Number( - document.querySelector( '#classifai-settings-category_threshold' ) - .value - ); - const keywordThreshold = Number( - document.querySelector( '#classifai-settings-keyword_threshold' ) - .value - ); - const entityThreshold = Number( - document.querySelector( '#classifai-settings-entity_threshold' ) - .value - ); - const conceptThreshold = Number( - document.querySelector( '#classifai-settings-concept_threshold' ) - .value - ); + /** + * Live preview features. + * + * @param {Object} e The event object. + */ + function showPreviewWatson( e ) { + /** Category thresholds. */ + const categoryThreshold = Number( + document.querySelector( '#classifai-settings-category_threshold' ) + .value + ); + const keywordThreshold = Number( + document.querySelector( '#classifai-settings-keyword_threshold' ) + .value + ); + const entityThreshold = Number( + document.querySelector( '#classifai-settings-entity_threshold' ) + .value + ); + const conceptThreshold = Number( + document.querySelector( '#classifai-settings-concept_threshold' ) + .value + ); - const postId = document.getElementById( - 'classifai-preview-post-selector' - ).value; + const postId = document.getElementById( + 'classifai-preview-post-selector' + ).value; - const previewWrapper = document.getElementById( - 'classifai-post-preview-wrapper' - ); - const thresholds = { - categories: categoryThreshold, - keywords: keywordThreshold, - entities: entityThreshold, - concepts: conceptThreshold, - }; + const previewWrapper = document.getElementById( + 'classifai-post-preview-wrapper' + ); + const thresholds = { + categories: categoryThreshold, + keywords: keywordThreshold, + entities: entityThreshold, + concepts: conceptThreshold, + }; - e.target - .closest( '.button' ) - .classList.add( 'get-classifier-preview-data-btn--loading' ); + e.target + .closest( '.button' ) + .classList.add( 'get-classifier-preview-data-btn--loading' ); - const formData = new FormData(); - formData.append( 'action', 'get_post_classifier_preview_data' ); - formData.append( 'post_id', postId ); - formData.append( 'nonce', previewerNonce ); + const formData = new FormData(); + formData.append( 'action', 'get_post_classifier_preview_data' ); + formData.append( 'post_id', postId ); + formData.append( 'nonce', previewerNonce ); - fetch( `${ ajaxurl }`, { - method: 'POST', - body: formData, - } ) + fetch( `${ ajaxurl }`, { + method: 'POST', + body: formData, + } ) .then( ( response ) => { return response.json(); } ) @@ -162,33 +171,113 @@ import '../scss/language-processing.scss'; 'get-classifier-preview-data-btn--loading' ); } ); - } + } + + /** + * Filters response data depending on the threshold value. + * + * @param {Object} data Response data from NLU. + * @param {Object} thresholds Object containing threshold values for various taxnomy types. + * @return {Array} Sorted data. + */ + function filterByScoreOrRelevance( data = {}, thresholds ) { + const filteredItems = Object.keys( data ).map( ( key ) => ( { + [ key ]: data[ key ].filter( ( item ) => { + if ( item?.score && item.score * 100 > thresholds[ key ] ) { + return item; + } else if ( + item?.relevance && + item.relevance * 100 > thresholds[ key ] + ) { + return item; + } - /** - * Filters response data depending on the threshold value. - * - * @param {Object} data Response data from NLU. - * @param {Object} thresholds Object containing threshold values for various taxnomy types. - * @return {Array} Sorted data. - */ - function filterByScoreOrRelevance( data = {}, thresholds ) { - const filteredItems = Object.keys( data ).map( ( key ) => ( { - [ key ]: data[ key ].filter( ( item ) => { - if ( item?.score && item.score * 100 > thresholds[ key ] ) { - return item; - } else if ( - item?.relevance && - item.relevance * 100 > thresholds[ key ] - ) { return item; + } ), + } ) ); + + return filteredItems; + } + }; + previewWatson(); + + const previewEmbeddings = () => { + const nonceElement = document.getElementById( 'classifai-previewer-openai_embeddings-nonce' ); + if ( ! nonceElement ) { + return; + } + + const getClassifierDataBtn = document.getElementById( + 'get-classifier-preview-data-btn' + ); + getClassifierDataBtn.addEventListener( 'click', showPreviewEmeddings ); + + /** Previewer nonce. */ + const previewerNonce = nonceElement.value; + + /** + * Live preview features. + * + * @param {Object} e The event object. + */ + function showPreviewEmeddings( e ) { + const postId = document.getElementById( + 'classifai-preview-post-selector' + ).value; + + const previewWrapper = document.getElementById( + 'classifai-post-preview-wrapper' + ); + + // clear previewWrapper. + previewWrapper.innerHTML = ''; + + e.target + .closest( '.button' ) + .classList.add( 'get-classifier-preview-data-btn--loading' ); + + const formData = new FormData(); + formData.append( 'action', 'get_post_classifier_embeddings_preview_data' ); + formData.append( 'post_id', postId ); + formData.append( 'nonce', previewerNonce ); + + fetch( `${ ajaxurl }`, { + method: 'POST', + body: formData, + } ) + .then( ( response ) => { + return response.json(); + } ) + .then( ( data ) => { + if ( ! data.success ) { + previewWrapper.style.display = 'block'; + previewWrapper.innerHTML = data.data; + e.target + .closest( '.button' ) + .classList.remove( + 'get-classifier-preview-data-btn--loading' + ); + return; } - return item; - } ), - } ) ); + const htmlData = buildPreviewUI( data.data ); + previewWrapper.style.display = 'block'; + previewWrapper.innerHTML = htmlData; - return filteredItems; - } + // remove all .tax-row--hide + document.querySelectorAll( '.tax-row--hide' ).forEach( ( item ) => { + item.classList.remove( 'tax-row--hide' ); + } ); + + e.target + .closest( '.button' ) + .classList.remove( + 'get-classifier-preview-data-btn--loading' + ); + } ); + } + }; + previewEmbeddings(); /** * Builds user readable HTML data from the response by NLU. @@ -198,7 +287,12 @@ import '../scss/language-processing.scss'; */ function buildPreviewUI( filteredItems = [] ) { let htmlData = ''; - filteredItems.forEach( ( obj ) => { + // check if filteredItems.forEach type is function + if ( ! filteredItems.forEach ) { + return ''; + } + + filteredItems.forEach && filteredItems.forEach( ( obj ) => { Object.keys( obj ).forEach( ( prop ) => { htmlData += `
{ const closePanelSelector = '#get-classifier-preview-data-btn'; cy.get( closePanelSelector ).click(); - // Check the term is received is visible. + // Check the term is received and visible. cy.get( '.tax-row--Category' ).should( 'be.visible' ); } ); From 6f777cfc263f01bdb2ed943b0bb916381fc45c5b Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Sat, 25 Nov 2023 00:06:36 +0530 Subject: [PATCH 09/22] fix e2e tests --- src/js/language-processing.js | 25 +++++++++++++------ ...lassify-content-openapi-embeddings.test.js | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/js/language-processing.js b/src/js/language-processing.js index eab591b2b..a7879de38 100644 --- a/src/js/language-processing.js +++ b/src/js/language-processing.js @@ -4,11 +4,20 @@ import '../scss/language-processing.scss'; ( () => { let featureStatuses = {}; + const nonceElementNLU = document.getElementById( + 'classifai-previewer-watson_nlu-nonce' + ); + + const nonceElementEmbeddings = document.getElementById( + 'classifai-previewer-openai_embeddings-nonce' + ); + + if ( ! nonceElementNLU && ! nonceElementEmbeddings ) { + return; + } + const previewWatson = () => { - const nonceElement = document.getElementById( - 'classifai-previewer-watson_nlu-nonce' - ); - if ( ! nonceElement ) { + if ( ! nonceElementNLU ) { return; } @@ -18,7 +27,7 @@ import '../scss/language-processing.scss'; getClassifierDataBtn.addEventListener( 'click', showPreviewWatson ); /** Previewer nonce. */ - const previewerNonce = nonceElement.value; + const previewerNonce = nonceElementNLU.value; /** Feature statuses. */ featureStatuses = { @@ -213,10 +222,10 @@ import '../scss/language-processing.scss'; previewWatson(); const previewEmbeddings = () => { - const nonceElement = document.getElementById( + const nonceElementEmbeddings = document.getElementById( 'classifai-previewer-openai_embeddings-nonce' ); - if ( ! nonceElement ) { + if ( ! nonceElementEmbeddings ) { return; } @@ -226,7 +235,7 @@ import '../scss/language-processing.scss'; getClassifierDataBtn.addEventListener( 'click', showPreviewEmeddings ); /** Previewer nonce. */ - const previewerNonce = nonceElement.value; + const previewerNonce = nonceElementEmbeddings.value; /** * Live preview features. diff --git a/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js b/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js index 3efaba493..ca5e4105d 100644 --- a/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js +++ b/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js @@ -96,7 +96,7 @@ describe( '[Language processing] Classify Content (OpenAI) Tests', () => { cy.get( closePanelSelector ).click(); // Check the term is received and visible. - cy.get( '.tax-row--Category' ).should( 'be.visible' ); + cy.get( '.tax-row--Category' ).should( 'exist' ); } ); it( 'Can create category and post and category will not get auto-assigned if feature turned off', () => { From 0060ddf7c81ca69965b0e6ca07380feb21052475 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Sat, 25 Nov 2023 00:10:09 +0530 Subject: [PATCH 10/22] fix eslint errors --- src/js/language-processing.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/js/language-processing.js b/src/js/language-processing.js index a7879de38..43d7e568d 100644 --- a/src/js/language-processing.js +++ b/src/js/language-processing.js @@ -7,7 +7,7 @@ import '../scss/language-processing.scss'; const nonceElementNLU = document.getElementById( 'classifai-previewer-watson_nlu-nonce' ); - + const nonceElementEmbeddings = document.getElementById( 'classifai-previewer-openai_embeddings-nonce' ); @@ -222,9 +222,6 @@ import '../scss/language-processing.scss'; previewWatson(); const previewEmbeddings = () => { - const nonceElementEmbeddings = document.getElementById( - 'classifai-previewer-openai_embeddings-nonce' - ); if ( ! nonceElementEmbeddings ) { return; } @@ -375,12 +372,6 @@ import '../scss/language-processing.scss'; * @param {Object} event Choices.js's 'search' event object. */ function searchPosts( event ) { - const nonceElementEmbeddings = document.getElementById( - 'classifai-previewer-openai_embeddings-nonce' - ); - const nonceElementNLU = document.getElementById( - 'classifai-previewer-watson_nlu-nonce' - ); const nonceElement = nonceElementEmbeddings ? nonceElementEmbeddings : nonceElementNLU; From 35aeebc9e91c2c32f3056c8aa46bbb8f3047a2a2 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Sat, 25 Nov 2023 00:17:11 +0530 Subject: [PATCH 11/22] typo fix --- .../classify-content-openapi-embeddings.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js b/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js index ca5e4105d..3eb4478e4 100644 --- a/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js +++ b/tests/cypress/integration/language-processing/classify-content-openapi-embeddings.test.js @@ -86,7 +86,7 @@ describe( '[Language processing] Classify Content (OpenAI) Tests', () => { } ); } ); - it( 'Can see the privew on settings page', () => { + it( 'Can see the preview on the settings page', () => { cy.visit( '/wp-admin/tools.php?page=classifai&tab=language_processing&provider=openai_embeddings' ); From 3e28c0c48d825e3ba1986307edcede3acbc0bfae Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Tue, 28 Nov 2023 15:53:48 +0530 Subject: [PATCH 12/22] correct the function doc --- includes/Classifai/Providers/OpenAI/Embeddings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index b21077f48..e249a40e7 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -394,7 +394,7 @@ public function supported_taxonomies() { } /** - * Get the list of post types for settings. + * Get the data to preview terms. * * @since 2.6.0 * From 28d14bf1bdbfa833966df6179b180cd046e68d8c Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Tue, 28 Nov 2023 16:26:24 +0530 Subject: [PATCH 13/22] dry run with a seprate function --- .../Classifai/Providers/OpenAI/Embeddings.php | 123 +++++++++++++----- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index e249a40e7..1d85fd0e2 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -455,11 +455,12 @@ public function generate_embeddings_for_post( $post_id, $dryrun = false ) { // Add terms to this item based on embedding data. if ( $embeddings && ! is_wp_error( $embeddings ) ) { - if ( ! $dryrun ) { + if ( $dryrun ) { + return $this->get_terms( $post_id, $embeddings, true ); + } else { update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); + return $this->set_terms( $post_id, $embeddings ); } - - return $this->set_terms( $post_id, $embeddings, $dryrun ); } } @@ -468,11 +469,80 @@ public function generate_embeddings_for_post( $post_id, $dryrun = false ) { * * @param int $post_id ID of post to set terms on. * @param array $embedding Embedding data. - * @param bool $dryrun Whether to run the process or just return the data. + */ + private function set_terms( int $post_id = 0, array $embedding = [] ) { + if ( ! $post_id || ! get_post( $post_id ) ) { + return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to set terms.', 'classifai' ) ); + } + + if ( empty( $embedding ) ) { + return new WP_Error( 'data_required', esc_html__( 'Valid embedding data is required to set terms.', 'classifai' ) ); + } + + $settings = $this->get_settings(); + $number_to_add = $settings['number'] ?? 1; + $embedding_similarity = []; + $taxonomies = $this->supported_taxonomies(); + $calculations = new EmbeddingCalculations(); + + foreach ( $taxonomies as $tax ) { + $terms = get_terms( + [ + 'taxonomy' => $tax, + 'hide_empty' => false, + 'fields' => 'ids', + 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + // 'number' => 500, TODO: see if we need a limit here. + ] + ); + + if ( is_wp_error( $terms ) || empty( $terms ) ) { + continue; + } + + // Get embedding similarity for each term. + foreach ( $terms as $term_id ) { + if ( ! current_user_can( 'assign_term', $term_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { + continue; + } + + $term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true ); + + if ( $term_embedding ) { + $similarity = $calculations->similarity( $embedding, $term_embedding ); + if ( false !== $similarity ) { + $embedding_similarity[ $tax ][ $term_id ] = $calculations->similarity( $embedding, $term_embedding ); + } + } + } + } + + if ( empty( $embedding_similarity ) ) { + return; + } + + foreach ( $embedding_similarity as $tax => $terms ) { + // Sort embeddings from lowest to highest. + asort( $terms ); + + // Only add the number of terms specified in settings. + if ( count( $terms ) > $number_to_add ) { + $terms = array_slice( $terms, 0, $number_to_add, true ); + } + + wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); + } + } + + /** + * Get the terms of a post based on embeddings. + * + * @param int $post_id ID of post to set terms on. + * @param array $embedding Embedding data. * * @return array|WP_Error */ - private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = false ) { + private function get_terms( int $post_id = 0, array $embedding = [] ) { if ( ! $post_id || ! get_post( $post_id ) ) { return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to set terms.', 'classifai' ) ); } @@ -494,7 +564,6 @@ private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = f 'hide_empty' => false, 'fields' => 'ids', 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - // 'number' => 500, TODO: see if we need a limit here. ] ); @@ -535,28 +604,26 @@ private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = f // Sort embeddings from lowest to highest. asort( $terms ); - // Return the terms if this is a dry run. - if ( $dryrun ) { - $result[ $index ] = new \stdClass(); + // Return the terms. + $result[ $index ] = new \stdClass(); - $result[ $index ]->{$tax_name} = []; + $result[ $index ]->{$tax_name} = []; - $term_added = 0; - foreach ( $terms as $term_id => $similarity ) { - // Convert $similarity to percentage. - $similarity = round( ( 1 - $similarity ), 2 ); + $term_added = 0; + foreach ( $terms as $term_id => $similarity ) { + // Convert $similarity to percentage. + $similarity = round( ( 1 - $similarity ), 2 ); - // Stop if we have added the number of terms specified in settings. - if ( $number_to_add <= $term_added ) { - break; - } - - $result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found - 'label' => get_term( $term_id )->name, - 'score' => $similarity, - ]; - $term_added++; + // Stop if we have added the number of terms specified in settings. + if ( $number_to_add <= $term_added ) { + break; } + + $result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found + 'label' => get_term( $term_id )->name, + 'score' => $similarity, + ]; + $term_added++; } // Only add the number of terms specified in settings. @@ -564,16 +631,10 @@ private function set_terms( int $post_id = 0, array $embedding = [], $dryrun = f $terms = array_slice( $terms, 0, $number_to_add, true ); } - if ( ! $dryrun ) { - wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); - } $index++; } - // Return if its a dry run. - if ( $dryrun ) { - return $result; - } + return $result; } /** From 38c5b995f70f73c4ab1c0b971bd53313a8d16759 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Tue, 28 Nov 2023 16:37:15 +0530 Subject: [PATCH 14/22] code improvements --- .../Classifai/Providers/OpenAI/Embeddings.php | 132 ++++++++---------- 1 file changed, 55 insertions(+), 77 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index 1d85fd0e2..820b1e712 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -456,7 +456,7 @@ public function generate_embeddings_for_post( $post_id, $dryrun = false ) { // Add terms to this item based on embedding data. if ( $embeddings && ! is_wp_error( $embeddings ) ) { if ( $dryrun ) { - return $this->get_terms( $post_id, $embeddings, true ); + return $this->get_terms( $embeddings ); } else { update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); return $this->set_terms( $post_id, $embeddings ); @@ -481,46 +481,13 @@ private function set_terms( int $post_id = 0, array $embedding = [] ) { $settings = $this->get_settings(); $number_to_add = $settings['number'] ?? 1; - $embedding_similarity = []; - $taxonomies = $this->supported_taxonomies(); - $calculations = new EmbeddingCalculations(); - - foreach ( $taxonomies as $tax ) { - $terms = get_terms( - [ - 'taxonomy' => $tax, - 'hide_empty' => false, - 'fields' => 'ids', - 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - // 'number' => 500, TODO: see if we need a limit here. - ] - ); - - if ( is_wp_error( $terms ) || empty( $terms ) ) { - continue; - } - - // Get embedding similarity for each term. - foreach ( $terms as $term_id ) { - if ( ! current_user_can( 'assign_term', $term_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { - continue; - } - - $term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true ); - - if ( $term_embedding ) { - $similarity = $calculations->similarity( $embedding, $term_embedding ); - if ( false !== $similarity ) { - $embedding_similarity[ $tax ][ $term_id ] = $calculations->similarity( $embedding, $term_embedding ); - } - } - } - } + $embedding_similarity = $this->get_embeddings_similarity( $embedding ); if ( empty( $embedding_similarity ) ) { return; } + // Set terms based on similarity. foreach ( $embedding_similarity as $tax => $terms ) { // Sort embeddings from lowest to highest. asort( $terms ); @@ -537,56 +504,18 @@ private function set_terms( int $post_id = 0, array $embedding = [] ) { /** * Get the terms of a post based on embeddings. * - * @param int $post_id ID of post to set terms on. * @param array $embedding Embedding data. * * @return array|WP_Error */ - private function get_terms( int $post_id = 0, array $embedding = [] ) { - if ( ! $post_id || ! get_post( $post_id ) ) { - return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to set terms.', 'classifai' ) ); - } - + private function get_terms( array $embedding = [] ) { if ( empty( $embedding ) ) { - return new WP_Error( 'data_required', esc_html__( 'Valid embedding data is required to set terms.', 'classifai' ) ); + return new WP_Error( 'data_required', esc_html__( 'Valid embedding data is required to get terms.', 'classifai' ) ); } $settings = $this->get_settings(); $number_to_add = $settings['number'] ?? 1; - $embedding_similarity = []; - $taxonomies = $this->supported_taxonomies(); - $calculations = new EmbeddingCalculations(); - - foreach ( $taxonomies as $tax ) { - $terms = get_terms( - [ - 'taxonomy' => $tax, - 'hide_empty' => false, - 'fields' => 'ids', - 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - ] - ); - - if ( is_wp_error( $terms ) || empty( $terms ) ) { - continue; - } - - // Get embedding similarity for each term. - foreach ( $terms as $term_id ) { - if ( ! current_user_can( 'assign_term', $term_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { - continue; - } - - $term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true ); - - if ( $term_embedding ) { - $similarity = $calculations->similarity( $embedding, $term_embedding ); - if ( false !== $similarity ) { - $embedding_similarity[ $tax ][ $term_id ] = $calculations->similarity( $embedding, $term_embedding ); - } - } - } - } + $embedding_similarity = $this->get_embeddings_similarity( $embedding ); if ( empty( $embedding_similarity ) ) { return; @@ -637,6 +566,55 @@ private function get_terms( int $post_id = 0, array $embedding = [] ) { return $result; } + /** + * Get the similarity between an embedding and all terms. + * + * @since 2.6.0 + * + * @param array $embedding Embedding data. + * + * @return array + */ + private function get_embeddings_similarity( $embedding ) { + $embedding_similarity = []; + $taxonomies = $this->supported_taxonomies(); + $calculations = new EmbeddingCalculations(); + + foreach ( $taxonomies as $tax ) { + $terms = get_terms( + [ + 'taxonomy' => $tax, + 'hide_empty' => false, + 'fields' => 'ids', + 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + // 'number' => 500, TODO: see if we need a limit here. + ] + ); + + if ( is_wp_error( $terms ) || empty( $terms ) ) { + continue; + } + + // Get embedding similarity for each term. + foreach ( $terms as $term_id ) { + if ( ! current_user_can( 'assign_term', $term_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { + continue; + } + + $term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true ); + + if ( $term_embedding ) { + $similarity = $calculations->similarity( $embedding, $term_embedding ); + if ( false !== $similarity ) { + $embedding_similarity[ $tax ][ $term_id ] = $calculations->similarity( $embedding, $term_embedding ); + } + } + } + } + + return $embedding_similarity; + } + /** * Generate embedding data for all terms within a taxonomy. * From 7bd994f8f91157f6d289bf69b3ef52b412ea12f9 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Tue, 28 Nov 2023 14:23:47 -0700 Subject: [PATCH 15/22] Update since version --- includes/Classifai/Providers/OpenAI/Embeddings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php index 820b1e712..e990359d4 100644 --- a/includes/Classifai/Providers/OpenAI/Embeddings.php +++ b/includes/Classifai/Providers/OpenAI/Embeddings.php @@ -396,7 +396,7 @@ public function supported_taxonomies() { /** * Get the data to preview terms. * - * @since 2.6.0 + * @since 2.5.0 * * @return array */ @@ -569,7 +569,7 @@ private function get_terms( array $embedding = [] ) { /** * Get the similarity between an embedding and all terms. * - * @since 2.6.0 + * @since 2.5.0 * * @param array $embedding Embedding data. * From e86a2139b06bb109790c03b6179288ed2cc6af6d Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Wed, 29 Nov 2023 15:39:24 +0530 Subject: [PATCH 16/22] remove unnecssary -ve left margin --- src/scss/language-processing.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scss/language-processing.scss b/src/scss/language-processing.scss index 53e202aa5..7bb41b7fa 100644 --- a/src/scss/language-processing.scss +++ b/src/scss/language-processing.scss @@ -123,7 +123,6 @@ align-self: flex-start; position: sticky; top: 3rem; - margin-left: -5rem; .choices__inner { display: block; From 4fd02c18cda5ef502442fd7108d821d90f66d29d Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Wed, 29 Nov 2023 15:40:23 +0530 Subject: [PATCH 17/22] fix the provide class selection --- includes/Classifai/Services/Service.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php index 0be1a6dfb..0c67e1a64 100644 --- a/includes/Classifai/Services/Service.php +++ b/includes/Classifai/Services/Service.php @@ -151,10 +151,16 @@ public function render_settings_page() { provider_classes ?? [], 'Natural Language Understanding' ); + if ( 'openai_embeddings' === $active_tab ) { + $provider = find_provider_class( $this->provider_classes ?? [], 'Embeddings' ); + } if ( - ! is_wp_error( $provider ) && ! empty( $provider->can_register() ) - && ( 'openai_embeddings' === $active_tab || $provider->is_feature_enabled( 'content_classification' ) ) + ! is_wp_error( $provider ) + && ! empty( + $provider->can_register() + && $provider->is_feature_enabled( 'content_classification' ) + ) ) : ?>
From d7dc76b99a95279a6a09ee813d88ae57e219dee0 Mon Sep 17 00:00:00 2001 From: faisal-alvi Date: Wed, 29 Nov 2023 15:43:54 +0530 Subject: [PATCH 18/22] handle the classification names --- includes/Classifai/Services/Service.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php index 0c67e1a64..59abd2ece 100644 --- a/includes/Classifai/Services/Service.php +++ b/includes/Classifai/Services/Service.php @@ -155,11 +155,15 @@ public function render_settings_page() { $provider = find_provider_class( $this->provider_classes ?? [], 'Embeddings' ); } + // echo '
';
+					// print_r( $provider );
+					// die();
+
 					if (
 						! is_wp_error( $provider )
 						&& ! empty(
 							$provider->can_register()
-							&& $provider->is_feature_enabled( 'content_classification' )
+							&& ( $provider->is_feature_enabled( 'content_classification' ) || $provider->is_feature_enabled( 'classification' ) )
 						)
 					) :
 						?>

From e04e66ec9ad0a8b296bc7a44047e2cba42a7d26d Mon Sep 17 00:00:00 2001
From: faisal-alvi 
Date: Wed, 29 Nov 2023 15:50:26 +0530
Subject: [PATCH 19/22] remove unnecessary code

---
 includes/Classifai/Services/Service.php | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php
index 59abd2ece..bc8f1a4ad 100644
--- a/includes/Classifai/Services/Service.php
+++ b/includes/Classifai/Services/Service.php
@@ -155,10 +155,6 @@ public function render_settings_page() {
 						$provider = find_provider_class( $this->provider_classes ?? [], 'Embeddings' );
 					}
 
-					// echo '
';
-					// print_r( $provider );
-					// die();
-
 					if (
 						! is_wp_error( $provider )
 						&& ! empty(

From ae667995c09c8a9ed3ae19bfe95f2755e38a7c11 Mon Sep 17 00:00:00 2001
From: Darin Kotter 
Date: Thu, 7 Dec 2023 11:59:32 -0700
Subject: [PATCH 20/22] Ensure we always trim our calculations results down to
 1.0 instead of the threshold value. After running the calculation, remove any
 results that are above our threshold. Use a higher level of precision when
 converting our decimals to percentages so we can more easily see the
 differences in each value.

---
 .../OpenAI/EmbeddingCalculations.php          | 19 +++----------------
 .../Classifai/Providers/OpenAI/Embeddings.php | 10 +++++-----
 2 files changed, 8 insertions(+), 21 deletions(-)

diff --git a/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php b/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
index 994037c65..7d41b162e 100644
--- a/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
+++ b/includes/Classifai/Providers/OpenAI/EmbeddingCalculations.php
@@ -15,11 +15,10 @@ class EmbeddingCalculations {
 	 *
 	 * @param array $source_embedding Embedding data of the source item.
 	 * @param array $compare_embedding Embedding data of the item to compare.
-	 * @param float $threshold The threshold to use for the similarity calculation.
 	 *
 	 * @return bool|float
 	 */
-	public function similarity( array $source_embedding = [], array $compare_embedding = [], $threshold = 1 ) {
+	public function similarity( array $source_embedding = [], array $compare_embedding = [] ) {
 		if ( empty( $source_embedding ) || empty( $compare_embedding ) ) {
 			return false;
 		}
@@ -58,20 +57,8 @@ function( $x ) {
 		// Do the math.
 		$distance = 1.0 - ( $combined_average / sqrt( $source_average * $compare_average ) );
 
-		/**
-		 * Filter the threshold for the similarity calculation.
-		 *
-		 * @since 2.5.0
-		 * @hook classifai_threshold
-		 *
-		 * @param {float} $threshold The threshold to use.
-		 *
-		 * @return {float} The threshold to use.
-		 */
-		$threshold = apply_filters( 'classifai_threshold', $threshold );
-
-		// Ensure we are within the range of 0 to 1.0 (i.e. $threshold).
-		return max( 0, min( abs( (float) $distance ), $threshold ) );
+		// Ensure we are within the range of 0 to 1.0.
+		return max( 0, min( abs( (float) $distance ), 1.0 ) );
 	}
 
 }
diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php
index 66ee10a36..39ef3f66b 100644
--- a/includes/Classifai/Providers/OpenAI/Embeddings.php
+++ b/includes/Classifai/Providers/OpenAI/Embeddings.php
@@ -604,14 +604,14 @@ private function get_terms( array $embedding = [] ) {
 
 			$term_added = 0;
 			foreach ( $terms as $term_id => $similarity ) {
-				// Convert $similarity to percentage.
-				$similarity = round( ( 1 - $similarity ), 2 );
-
 				// Stop if we have added the number of terms specified in settings.
 				if ( $number_to_add <= $term_added ) {
 					break;
 				}
 
+				// Convert $similarity to percentage.
+				$similarity = round( ( 1 - $similarity ), 10 );
+
 				$result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
 					'label' => get_term( $term_id )->name,
 					'score' => $similarity,
@@ -671,8 +671,8 @@ private function get_embeddings_similarity( $embedding ) {
 				$term_embedding = get_term_meta( $term_id, 'classifai_openai_embeddings', true );
 
 				if ( $term_embedding ) {
-					$similarity = $calculations->similarity( $embedding, $term_embedding, $threshold );
-					if ( false !== $similarity ) {
+					$similarity = $calculations->similarity( $embedding, $term_embedding );
+					if ( false !== $similarity || $similarity <= $threshold ) {
 						$embedding_similarity[ $tax ][ $term_id ] = $similarity;
 					}
 				}

From bcee113466b0e34513c96de21b963b62c6ef7fd4 Mon Sep 17 00:00:00 2001
From: faisal-alvi 
Date: Fri, 8 Dec 2023 17:47:57 +0530
Subject: [PATCH 21/22] fix the issue

---
 includes/Classifai/Providers/OpenAI/Embeddings.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php
index 39ef3f66b..7789aca9b 100644
--- a/includes/Classifai/Providers/OpenAI/Embeddings.php
+++ b/includes/Classifai/Providers/OpenAI/Embeddings.php
@@ -672,7 +672,7 @@ private function get_embeddings_similarity( $embedding ) {
 
 				if ( $term_embedding ) {
 					$similarity = $calculations->similarity( $embedding, $term_embedding );
-					if ( false !== $similarity || $similarity <= $threshold ) {
+					if ( false !== $similarity && $similarity <= $threshold ) {
 						$embedding_similarity[ $tax ][ $term_id ] = $similarity;
 					}
 				}

From df0d6771bc7387e88d4a024a056210d1acd48884 Mon Sep 17 00:00:00 2001
From: faisal-alvi 
Date: Fri, 8 Dec 2023 17:53:23 +0530
Subject: [PATCH 22/22] dont consider threshold for preview

---
 includes/Classifai/Providers/OpenAI/Embeddings.php | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/includes/Classifai/Providers/OpenAI/Embeddings.php b/includes/Classifai/Providers/OpenAI/Embeddings.php
index 7789aca9b..d6415fd47 100644
--- a/includes/Classifai/Providers/OpenAI/Embeddings.php
+++ b/includes/Classifai/Providers/OpenAI/Embeddings.php
@@ -579,7 +579,7 @@ private function get_terms( array $embedding = [] ) {
 
 		$settings             = $this->get_settings();
 		$number_to_add        = $settings['number'] ?? 1;
-		$embedding_similarity = $this->get_embeddings_similarity( $embedding );
+		$embedding_similarity = $this->get_embeddings_similarity( $embedding, false );
 
 		if ( empty( $embedding_similarity ) ) {
 			return;
@@ -636,10 +636,11 @@ private function get_terms( array $embedding = [] ) {
 	 * @since 2.5.0
 	 *
 	 * @param array $embedding Embedding data.
+	 * @param bool  $consider_threshold Whether to consider the threshold setting.
 	 *
 	 * @return array
 	 */
-	private function get_embeddings_similarity( $embedding ) {
+	private function get_embeddings_similarity( $embedding, $consider_threshold = true ) {
 		$embedding_similarity = [];
 		$taxonomies           = $this->supported_taxonomies();
 		$calculations         = new EmbeddingCalculations();
@@ -672,7 +673,7 @@ private function get_embeddings_similarity( $embedding ) {
 
 				if ( $term_embedding ) {
 					$similarity = $calculations->similarity( $embedding, $term_embedding );
-					if ( false !== $similarity && $similarity <= $threshold ) {
+					if ( false !== $similarity && ( ! $consider_threshold || $similarity <= $threshold ) ) {
 						$embedding_similarity[ $tax ][ $term_id ] = $similarity;
 					}
 				}