From f180a0adaf88917ff6ad894c8ece8d5741c999f9 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 13:51:03 -0700 Subject: [PATCH 1/8] Allow ChatGPT to be used as a Provider for Descriptive Text Generation --- includes/Classifai/Features/DescriptiveTextGenerator.php | 9 +++++++++ includes/Classifai/Services/ImageProcessing.php | 1 + 2 files changed, 10 insertions(+) diff --git a/includes/Classifai/Features/DescriptiveTextGenerator.php b/includes/Classifai/Features/DescriptiveTextGenerator.php index 0002a4694..2e6fa3626 100644 --- a/includes/Classifai/Features/DescriptiveTextGenerator.php +++ b/includes/Classifai/Features/DescriptiveTextGenerator.php @@ -3,6 +3,7 @@ namespace Classifai\Features; use Classifai\Providers\Azure\ComputerVision; +use Classifai\Providers\OpenAI\ChatGPT; use Classifai\Services\ImageProcessing; use WP_REST_Server; use WP_REST_Request; @@ -21,6 +22,13 @@ class DescriptiveTextGenerator extends Feature { */ const ID = 'feature_descriptive_text_generator'; + /** + * Prompt for generating descriptive text. + * + * @var string + */ + public $prompt = 'You are an assistant that generates descriptions of images posted on a website. You will be provided with an image and will describe the main item you see in the image, giving details but staying concise. There is no need to say "the image contains" or similar, just describe what is actually in the image.'; + /** * Constructor. */ @@ -33,6 +41,7 @@ public function __construct() { // Contains just the providers this feature supports. $this->supported_providers = [ ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ), + ChatGPT::ID => __( 'OpenAI', 'classifai' ), ]; } diff --git a/includes/Classifai/Services/ImageProcessing.php b/includes/Classifai/Services/ImageProcessing.php index 981f22f19..bd508ea17 100644 --- a/includes/Classifai/Services/ImageProcessing.php +++ b/includes/Classifai/Services/ImageProcessing.php @@ -55,6 +55,7 @@ public static function get_service_providers(): array { 'classifai_image_processing_service_providers', [ 'Classifai\Providers\Azure\ComputerVision', + 'Classifai\Providers\OpenAI\ChatGPT', 'Classifai\Providers\OpenAI\DallE', ] ); From 71f6993662e4681cfbcf2f905fc632771b4f96e1 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 13:54:20 -0700 Subject: [PATCH 2/8] Add a route to handle the descriptive text generation request --- .../Classifai/Providers/OpenAI/ChatGPT.php | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 6a50aab76..3c8b6a208 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -6,6 +6,7 @@ namespace Classifai\Providers\OpenAI; use Classifai\Features\ContentResizing; +use Classifai\Features\DescriptiveTextGenerator; use Classifai\Features\ExcerptGeneration; use Classifai\Features\TitleGeneration; use Classifai\Providers\Provider; @@ -14,6 +15,8 @@ use function Classifai\get_default_prompt; use function Classifai\sanitize_number_of_responses_field; +use function Classifai\get_modified_image_source_url; +use function Classifai\get_largest_size_and_dimensions_image_url; class ChatGPT extends Provider { @@ -189,6 +192,9 @@ public function rest_endpoint_callback( $post_id = 0, string $route_to_call = '' // Handle all of our routes. switch ( $route_to_call ) { + case 'descriptive_text': + $return = $this->generate_descriptive_text( $post_id, $args ); + break; case 'excerpt': $return = $this->generate_excerpt( $post_id, $args ); break; @@ -203,6 +209,130 @@ public function rest_endpoint_callback( $post_id = 0, string $route_to_call = '' return $return; } + /** + * Generate descriptive text of an image. + * + * @param int $post_id The attachment ID we're processing. + * @param array $args Optional arguments. + * @return string|WP_Error + */ + public function generate_descriptive_text( int $post_id = 0, array $args = [] ) { + // Check to be sure the attachment exists and is an image. + if ( ! wp_attachment_is_image( $post_id ) ) { + return new WP_Error( 'invalid', esc_html__( 'This attachment can\'t be processed.', 'classifai' ) ); + } + + $metadata = wp_get_attachment_metadata( $post_id ); + + if ( ! $metadata || ! is_array( $metadata ) ) { + return new WP_Error( 'invalid', esc_html__( 'No valid metadata found.', 'classifai' ) ); + } + + $image_url = get_modified_image_source_url( $post_id ); + + if ( empty( $image_url ) || ! filter_var( $image_url, FILTER_VALIDATE_URL ) ) { + if ( isset( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ) { + $image_url = get_largest_size_and_dimensions_image_url( + get_attached_file( $post_id ), + wp_get_attachment_url( $post_id ), + $metadata, + [ 512, 2000 ], + [ 512, 2000 ], + 100 * MB_IN_BYTES + ); + } else { + $image_url = wp_get_attachment_url( $post_id ); + } + } + + if ( empty( $image_url ) ) { + return new WP_Error( 'error', esc_html__( 'Valid image size not found. Make sure the image is bigger than 512x512px.', 'classifai' ) ); + } + + $feature = new DescriptiveTextGenerator(); + $settings = $feature->get_settings(); + + // These checks (and the one above) happen in the REST permission_callback, + // but we run them again here in case this method is called directly. + if ( empty( $settings ) || ( isset( $settings[ static::ID ]['authenticated'] ) && false === $settings[ static::ID ]['authenticated'] ) || ( ! $feature->is_feature_enabled() && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) { + return new WP_Error( 'not_enabled', esc_html__( 'Descriptive text generation is disabled or OpenAI authentication failed. Please check your settings.', 'classifai' ) ); + } + + $request = new APIRequest( $settings[ static::ID ]['api_key'] ?? '', $feature->get_option_name() ); + + /** + * Filter the prompt we will send to ChatGPT. + * + * @since 3.2.0 + * @hook classifai_chatgpt_descriptive_text_prompt + * + * @param {string} $prompt Prompt we are sending to ChatGPT. + * @param {int} $post_id ID of attachment we are describing. + * + * @return {string} Prompt. + */ + $prompt = apply_filters( 'classifai_chatgpt_descriptive_text_prompt', get_default_prompt( $settings['prompt'] ?? [] ) ?? $feature->prompt, $post_id ); + + /** + * Filter the request body before sending to ChatGPT. + * + * @since 3.2.0 + * @hook classifai_chatgpt_descriptive_text_request_body + * + * @param {array} $body Request body that will be sent to ChatGPT. + * @param {int} $post_id ID of attachment we are describing. + * + * @return {array} Request body. + */ + $body = apply_filters( + 'classifai_chatgpt_descriptive_text_request_body', + [ + 'model' => $this->chatgpt_model, + 'messages' => [ + [ + 'role' => 'system', + 'content' => $prompt, + ], + [ + 'role' => 'user', + 'content' => [ + [ + 'type' => 'image_url', + 'image_url' => [ + 'url' => $image_url, + 'detail' => 'high', + ], + ], + ], + ], + ], + 'temperature' => 0.2, + 'max_tokens' => 300, + ], + $post_id + ); + + // Make our API request. + $response = $request->post( + $this->chatgpt_url, + [ + 'body' => wp_json_encode( $body ), + ] + ); + + // Extract out the text response, if it exists. + if ( ! is_wp_error( $response ) && ! empty( $response['choices'] ) ) { + foreach ( $response['choices'] as $choice ) { + if ( isset( $choice['message'], $choice['message']['content'] ) ) { + // ChatGPT often adds quotes to strings, so remove those as well as extra spaces. + $response = sanitize_text_field( trim( $choice['message']['content'], ' "\'' ) ); + } + } + } + + return $response; + } + /** * Generate an excerpt using ChatGPT. * From eac68e89b0ab423be1fedccdedb817b5b9b9fc3b Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 15:04:38 -0700 Subject: [PATCH 3/8] Allow customizing the prompt in the settings. Fix typo --- .../Classifai/Providers/OpenAI/ChatGPT.php | 13 ++++++++++- .../content-resizing.js | 4 ++-- .../excerpt-generation.js | 4 ++-- .../prompt-repeater.js | 8 +++---- .../title-generation.js | 4 ++-- .../provider-settings/openai-chatgpt.js | 22 +++++++++++++++++++ 6 files changed, 44 insertions(+), 11 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 3c8b6a208..4677e2616 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -135,6 +135,17 @@ public function get_default_provider_settings(): array { case TitleGeneration::ID: $common_settings['number_of_suggestions'] = 1; break; + + case DescriptiveTextGenerator::ID: + $common_settings['prompt'] = [ + [ + 'title' => esc_html__( 'ClassifAI default', 'classifai' ), + 'prompt' => $this->feature_instance->prompt, + 'original' => 1, + 'default' => 1, + ], + ]; + break; } return $common_settings; @@ -271,7 +282,7 @@ public function generate_descriptive_text( int $post_id = 0, array $args = [] ) * * @return {string} Prompt. */ - $prompt = apply_filters( 'classifai_chatgpt_descriptive_text_prompt', get_default_prompt( $settings['prompt'] ?? [] ) ?? $feature->prompt, $post_id ); + $prompt = apply_filters( 'classifai_chatgpt_descriptive_text_prompt', get_default_prompt( $settings[ static::ID ]['prompt'] ?? [] ) ?? $feature->prompt, $post_id ); /** * Filter the request body before sending to ChatGPT. diff --git a/src/js/settings/components/feature-additional-settings/content-resizing.js b/src/js/settings/components/feature-additional-settings/content-resizing.js index a312112ab..51d1a9610 100644 --- a/src/js/settings/components/feature-additional-settings/content-resizing.js +++ b/src/js/settings/components/feature-additional-settings/content-resizing.js @@ -33,7 +33,7 @@ export const ContentResizingSettings = () => { > { + setPrompts={ ( prompts ) => { setFeatureSettings( { condense_text_prompt: prompts, } ); @@ -47,7 +47,7 @@ export const ContentResizingSettings = () => { > { + setPrompts={ ( prompts ) => { setFeatureSettings( { expand_text_prompt: prompts, } ); diff --git a/src/js/settings/components/feature-additional-settings/excerpt-generation.js b/src/js/settings/components/feature-additional-settings/excerpt-generation.js index df1cf535e..be1ee790b 100644 --- a/src/js/settings/components/feature-additional-settings/excerpt-generation.js +++ b/src/js/settings/components/feature-additional-settings/excerpt-generation.js @@ -29,7 +29,7 @@ export const ExcerptGenerationSettings = () => { ); const { excerptPostTypesOptions } = usePostTypes(); const { setFeatureSettings } = useDispatch( STORE_NAME ); - const setPromts = ( prompts ) => { + const setPrompts = ( prompts ) => { setFeatureSettings( { generate_excerpt_prompt: prompts, } ); @@ -46,7 +46,7 @@ export const ExcerptGenerationSettings = () => { > { const [ showConfirmDialog, setShowConfirmDialog ] = useState( false ); const [ activeIndex, setActiveIndex ] = useState( null ); - const { prompts = [], setPromts } = props; + const { prompts = [], setPrompts } = props; const placeholder = prompts?.filter( ( prompt ) => prompt.original )[ 0 ]?.prompt || ''; // Add a new prompt. const addPrompt = () => { - setPromts( [ + setPrompts( [ ...prompts, { default: 0, original: 0, prompt: '', title: '' }, ] ); @@ -42,7 +42,7 @@ export const PromptRepeater = ( props ) => { if ( prompt[ 0 ]?.default ) { prompts[ 0 ].default = 1; } - setPromts( [ ...prompts ] ); + setPrompts( [ ...prompts ] ); }; // Update prompt. @@ -60,7 +60,7 @@ export const PromptRepeater = ( props ) => { ...prompts[ index ], ...changes, }; - setPromts( [ ...prompts ] ); + setPrompts( [ ...prompts ] ); }; // Confirm dialog to remove prompt. diff --git a/src/js/settings/components/feature-additional-settings/title-generation.js b/src/js/settings/components/feature-additional-settings/title-generation.js index e696434e3..dc279bc73 100644 --- a/src/js/settings/components/feature-additional-settings/title-generation.js +++ b/src/js/settings/components/feature-additional-settings/title-generation.js @@ -23,7 +23,7 @@ export const TitleGenerationSettings = () => { select( STORE_NAME ).getFeatureSettings() ); const { setFeatureSettings } = useDispatch( STORE_NAME ); - const setPromts = ( prompts ) => { + const setPrompts = ( prompts ) => { setFeatureSettings( { generate_title_prompt: prompts, } ); @@ -39,7 +39,7 @@ export const TitleGenerationSettings = () => { > ); diff --git a/src/js/settings/components/provider-settings/openai-chatgpt.js b/src/js/settings/components/provider-settings/openai-chatgpt.js index 12cf8191b..1721dab71 100644 --- a/src/js/settings/components/provider-settings/openai-chatgpt.js +++ b/src/js/settings/components/provider-settings/openai-chatgpt.js @@ -12,6 +12,7 @@ import { __ } from '@wordpress/i18n'; import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; import { useFeatureContext } from '../feature-settings/context'; +import { PromptRepeater } from '../feature-additional-settings/prompt-repeater'; /** * Component for OpenAI ChatGPT Provider settings. @@ -32,6 +33,11 @@ export const OpenAIChatGPTSettings = ( { isConfigured = false } ) => { ); const { setProviderSettings } = useDispatch( STORE_NAME ); const onChange = ( data ) => setProviderSettings( providerName, data ); + const setPrompts = ( prompts ) => { + setProviderSettings( providerName, { + prompt: prompts, + } ); + }; const Description = () => ( <> @@ -82,6 +88,22 @@ export const OpenAIChatGPTSettings = ( { isConfigured = false } ) => { /> ) } + { [ 'feature_descriptive_text_generator' ].includes( + featureName + ) && ( + + + + ) } ); }; From 41b1c61434fef13f0454eea7561b006390ccb515 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 15:10:50 -0700 Subject: [PATCH 4/8] Modify the default prompt a bit --- includes/Classifai/Features/DescriptiveTextGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/Classifai/Features/DescriptiveTextGenerator.php b/includes/Classifai/Features/DescriptiveTextGenerator.php index 2e6fa3626..8242f64c1 100644 --- a/includes/Classifai/Features/DescriptiveTextGenerator.php +++ b/includes/Classifai/Features/DescriptiveTextGenerator.php @@ -27,7 +27,7 @@ class DescriptiveTextGenerator extends Feature { * * @var string */ - public $prompt = 'You are an assistant that generates descriptions of images posted on a website. You will be provided with an image and will describe the main item you see in the image, giving details but staying concise. There is no need to say "the image contains" or similar, just describe what is actually in the image.'; + public $prompt = 'You are an assistant that generates descriptions of images that are used on a website. You will be provided with an image and will describe the main item you see in the image, giving details but staying concise. There is no need to say "the image contains" or similar, just describe what is actually in the image. This text will be important for screen readers, so make sure it is descriptive and accurate but not overly verbose.'; /** * Constructor. From 51ce65a0e21ff325b659d39a7516221a87291f56 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 15:36:18 -0700 Subject: [PATCH 5/8] Add E2E tests --- .../image-processing-openai-chatgpt.test.js | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 tests/cypress/integration/image-processing/image-processing-openai-chatgpt.test.js diff --git a/tests/cypress/integration/image-processing/image-processing-openai-chatgpt.test.js b/tests/cypress/integration/image-processing/image-processing-openai-chatgpt.test.js new file mode 100644 index 000000000..10b560105 --- /dev/null +++ b/tests/cypress/integration/image-processing/image-processing-openai-chatgpt.test.js @@ -0,0 +1,235 @@ +import { getChatGPTData } from '../../plugins/functions'; + +describe( 'OpenAI Image Processing Tests', () => { + let imageEditLink = ''; + let mediaModelLink = ''; + + before( () => { + cy.login(); + + const imageProcessingFeatures = [ + 'feature_descriptive_text_generator', + ]; + + imageProcessingFeatures.forEach( ( feature ) => { + cy.visitFeatureSettings( `image_processing/${ feature }` ); + cy.enableFeature(); + cy.selectProvider( 'openai_chatgpt' ); + cy.get( '#openai_chatgpt_api_key' ).clear().type( 'password' ); + cy.allowFeatureToAdmin(); + cy.get( '.classifai-settings__user-based-opt-out input' ).uncheck(); + + // Disable access for all users. + cy.disableFeatureForUsers(); + + cy.saveFeatureSettings(); + } ); + + cy.optInAllFeatures(); + } ); + + beforeEach( () => { + cy.login(); + } ); + + it( 'Can see Image Processing actions on edit media page and verify generated data.', () => { + cy.visitFeatureSettings( + 'image_processing/feature_descriptive_text_generator' + ); + cy.get( '.classifai-descriptive-text-fields input#alt' ).check(); + cy.saveFeatureSettings(); + cy.visit( '/wp-admin/upload.php?mode=grid' ); // Ensure grid mode is enabled. + cy.visit( '/wp-admin/media-new.php' ); + cy.get( '#plupload-upload-ui' ).should( 'exist' ); + cy.get( '#plupload-upload-ui input[type=file]' ).attachFile( + '../../../assets/img/onboarding-1.png' + ); + + cy.get( '#media-items .media-item a.edit-attachment', { + timeout: 20000, + } ).should( 'exist' ); + cy.get( '#media-items .media-item a.edit-attachment' ) + .invoke( 'attr', 'href' ) + .then( ( editLink ) => { + imageEditLink = editLink; + cy.visit( editLink ); + } ); + + // Verify Metabox with Image processing actions. + cy.get( '.postbox-header h2, #classifai_image_processing h2' ) + .first() + .contains( 'ClassifAI Image Processing' ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).contains( 'No descriptive text? Rescan image' ); + + // Verify generated Data. + const imageData = getChatGPTData(); + cy.get( '#attachment_alt' ).should( 'have.value', imageData ); + } ); + + it( 'Can see Image Processing actions on media modal', () => { + const imageId = imageEditLink.split( 'post=' )[ 1 ]?.split( '&' )[ 0 ]; + mediaModelLink = `wp-admin/upload.php?item=${ imageId }`; + cy.visit( mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + + // Verify Image processing actions. + cy.get( '#classifai-rescan-alt-tags' ).contains( 'Rescan' ); + } ); + + it( 'Can disable Image Processing features', () => { + const options = { + imageEditLink, + mediaModelLink, + }; + + // Disable features + cy.visitFeatureSettings( + 'image_processing/feature_descriptive_text_generator' + ); + cy.wait( 1000 ); + cy.get( '.classifai-descriptive-text-fields input#alt' ).uncheck(); + cy.get( '.classifai-descriptive-text-fields input#caption' ).uncheck(); + cy.get( + '.classifai-descriptive-text-fields input#description' + ).uncheck(); + cy.saveFeatureSettings(); + + // Verify that the feature is not available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'not.exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'not.exist' ); + + // Enable features. + cy.visitFeatureSettings( + 'image_processing/feature_descriptive_text_generator' + ); + cy.get( '.classifai-descriptive-text-fields input#alt' ).check(); + cy.get( '.classifai-descriptive-text-fields input#caption' ).check(); + cy.get( + '.classifai-descriptive-text-fields input#description' + ).check(); + cy.wait( 1500 ); + cy.enableFeature(); + cy.saveFeatureSettings(); + + // Verify that the feature is available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'exist' ); + } ); + + it( 'Can enable/disable Image Processing features by roles', () => { + const options = { + imageEditLink, + mediaModelLink, + }; + + // Enable features. + cy.visitFeatureSettings( + 'image_processing/feature_descriptive_text_generator' + ); + cy.enableFeature(); + cy.get( '.classifai-descriptive-text-fields input#alt' ).check(); + cy.wait( 500 ); + cy.saveFeatureSettings(); + + // Disable access to admin role. + cy.disableFeatureForRoles( 'feature_descriptive_text_generator', [ + 'administrator', + ] ); + + // Verify that the feature is not available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'not.exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'not.exist' ); + } ); + + it( 'Can enable/disable Image Processing features by user', () => { + const options = { + imageEditLink, + mediaModelLink, + }; + + // Disable access to admin role. + cy.disableFeatureForRoles( 'feature_descriptive_text_generator', [ + 'administrator', + ] ); + + // Verify that the feature is not available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'not.exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'not.exist' ); + + cy.enableFeatureForUsers( 'feature_descriptive_text_generator', [ + 'admin', + ] ); + + // Verify that the feature is available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'exist' ); + } ); + + it( 'User can opt-out of Image Processing features', () => { + const options = { + imageEditLink, + mediaModelLink, + }; + + // Enable user based opt-out. + cy.enableFeatureOptOut( 'feature_descriptive_text_generator' ); + + // opt-out + cy.optOutFeature( 'feature_descriptive_text_generator' ); + + // Verify that the feature is not available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'not.exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'not.exist' ); + + // opt-in + cy.optInFeature( 'feature_descriptive_text_generator' ); + + // Verify that the feature is available. + cy.wait( 1000 ); + cy.visit( options.imageEditLink ); + cy.get( + '#classifai_image_processing label[for=rescan-captions]' + ).should( 'exist' ); + cy.visit( options.mediaModelLink ); + cy.get( '.media-modal' ).should( 'exist' ); + cy.get( '#classifai-rescan-alt-tags' ).should( 'exist' ); + } ); +} ); From 02fc82998d4ff84c00bb7377809091815f296fbe Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 15:43:18 -0700 Subject: [PATCH 6/8] Set detail to auto --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 4677e2616..7e126b11f 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -311,7 +311,7 @@ public function generate_descriptive_text( int $post_id = 0, array $args = [] ) 'type' => 'image_url', 'image_url' => [ 'url' => $image_url, - 'detail' => 'high', + 'detail' => 'auto', ], ], ], From e76d9893ac245017bbd46f41a0012b1488b22454 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 15:50:53 -0700 Subject: [PATCH 7/8] Remove test that isn't needed --- .../Providers/Azure/ComputerVisionTest.php | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/tests/Classifai/Providers/Azure/ComputerVisionTest.php b/tests/Classifai/Providers/Azure/ComputerVisionTest.php index e20a2e9c9..d08eba908 100644 --- a/tests/Classifai/Providers/Azure/ComputerVisionTest.php +++ b/tests/Classifai/Providers/Azure/ComputerVisionTest.php @@ -60,40 +60,6 @@ public function test_smart_crop_image() { remove_filter( 'classifai_should_smart_crop_image', '__return_true' ); } - /** - * Ensure that settings returns default settings array if the `classifai_computer_vision` is not set. - */ - public function test_no_computer_vision_option_set() { - delete_option( 'classifai_computer_vision' ); - - $defaults = []; - - $expected = array_merge( - $defaults, - [ - 'status' => '0', - 'roles' => [], - 'users' => [], - 'user_based_opt_out' => 'no', - 'descriptive_text_fields' => [ - 'alt' => 'alt', - 'caption' => 0, - 'description' => 0, - ], - 'provider' => 'ms_computer_vision', - 'ms_computer_vision' => [ - 'endpoint_url' => '', - 'api_key' => '', - 'authenticated' => false, - 'descriptive_confidence_threshold' => 55, - ], - ] - ); - $settings = ( new \Classifai\Features\DescriptiveTextGenerator() )->get_settings(); - - $this->assertSame( $expected, $settings ); - } - /** * Ensure that attachment meta is being set. */ From 1860aba3d99b0e05167f5f6dc51abdf35fc8a030 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 21 Nov 2024 16:11:49 -0700 Subject: [PATCH 8/8] Bring over test fixes from 815 --- .../image-processing-microsoft-azure.test.js | 12 ++++++++++++ tests/cypress/support/commands.js | 1 + 2 files changed, 13 insertions(+) diff --git a/tests/cypress/integration/image-processing/image-processing-microsoft-azure.test.js b/tests/cypress/integration/image-processing/image-processing-microsoft-azure.test.js index 1d0fd3af1..7bfa6ecca 100644 --- a/tests/cypress/integration/image-processing/image-processing-microsoft-azure.test.js +++ b/tests/cypress/integration/image-processing/image-processing-microsoft-azure.test.js @@ -205,12 +205,15 @@ describe( 'Image processing Tests', () => { cy.disableFeatureForRoles( 'feature_descriptive_text_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_tags_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_cropping', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_to_text_generator', [ 'administrator', ] ); @@ -223,12 +226,15 @@ describe( 'Image processing Tests', () => { cy.enableFeatureForRoles( 'feature_descriptive_text_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.enableFeatureForRoles( 'feature_image_tags_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.enableFeatureForRoles( 'feature_image_cropping', [ 'administrator', ] ); + cy.wait( 500 ); cy.enableFeatureForRoles( 'feature_image_to_text_generator', [ 'administrator', ] ); @@ -248,12 +254,15 @@ describe( 'Image processing Tests', () => { cy.disableFeatureForRoles( 'feature_descriptive_text_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_tags_generator', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_cropping', [ 'administrator', ] ); + cy.wait( 500 ); cy.disableFeatureForRoles( 'feature_image_to_text_generator', [ 'administrator', ] ); @@ -265,8 +274,11 @@ describe( 'Image processing Tests', () => { cy.enableFeatureForUsers( 'feature_descriptive_text_generator', [ 'admin', ] ); + cy.wait( 500 ); cy.enableFeatureForUsers( 'feature_image_tags_generator', [ 'admin' ] ); + cy.wait( 500 ); cy.enableFeatureForUsers( 'feature_image_cropping', [ 'admin' ] ); + cy.wait( 500 ); cy.enableFeatureForUsers( 'feature_image_to_text_generator', [ 'admin', ] ); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 4bc718ac9..40b187f84 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -188,6 +188,7 @@ Cypress.Commands.add( 'disableFeatureForRoles', ( feature, roles ) => { // Disable access for all users. cy.disableFeatureForUsers(); + cy.wait( 100 ); cy.saveFeatureSettings(); } );