diff --git a/SitiWebUpdater.php b/SitiWebUpdater.php index 9f5c1d1..ef55b8b 100644 --- a/SitiWebUpdater.php +++ b/SitiWebUpdater.php @@ -119,7 +119,8 @@ class SitiWebUpdater { 'url' => $this->plugin["PluginURI"], 'slug' => $slug, 'package' => $new_files, - 'new_version' => $latest_version + 'new_version' => $latest_version, + 'icons' => $this->prepare_icon_set(), ); $transient->response[$this->basename] = (object) $plugin; // Return it in response @@ -160,10 +161,11 @@ class SitiWebUpdater { 'homepage' => $this->plugin["PluginURI"], 'short_description' => $this->plugin["Description"], 'sections' => array( - 'Description' => $this->plugin["Description"], - 'Updates' => $this->github_response['body'], + 'description' => wp_kses_post( wpautop( $this->plugin["Description"] ) ), + 'changelog' => $this->get_release_notes_html(), ), - 'download_link' => $this->github_response['zipball_url'] + 'download_link' => $this->github_response['zipball_url'], + 'icons' => $this->prepare_icon_set(), ); return (object) $plugin; // Return the data @@ -199,4 +201,37 @@ class SitiWebUpdater { return $result; } + + private function get_release_notes_html() { + $notes = isset( $this->github_response['body'] ) ? (string) $this->github_response['body'] : ''; + + if ( '' === trim( $notes ) ) { + return __( 'Nog geen changelog beschikbaar.', 'siti-ai-product-content-generator' ); + } + + return wp_kses_post( wpautop( $notes ) ); + } + + private function get_plugin_icon_url() { + $file = plugin_dir_path( $this->file ) . 'assets/images/plugin-icon.svg'; + if ( ! file_exists( $file ) ) { + return ''; + } + + return plugins_url( 'assets/images/plugin-icon.svg', $this->file ); + } + + private function prepare_icon_set() { + $icon_url = $this->get_plugin_icon_url(); + if ( '' === $icon_url ) { + return array(); + } + + return array( + '1x' => $icon_url, + '2x' => $icon_url, + 'svg' => $icon_url, + 'default' => $icon_url, + ); + } } diff --git a/assets/images/plugin-icon.svg b/assets/images/plugin-icon.svg new file mode 100644 index 0000000..1196777 --- /dev/null +++ b/assets/images/plugin-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + SitiAI + diff --git a/groq-ai-product-text.php b/groq-ai-product-text.php index 615437b..e07ee56 100644 --- a/groq-ai-product-text.php +++ b/groq-ai-product-text.php @@ -2,7 +2,7 @@ /** * Plugin Name: SitiAI Product Teksten * Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce. - * Version: 1.7.0 + * Version: 1.8.0 * Author: SitiAI * Text Domain: siti-ai-product-content-generator * Domain Path: /languages @@ -350,6 +350,22 @@ final class Groq_AI_Product_Text_Plugin { return $this->get_settings_manager()->get_term_bottom_description_char_limit( $settings ); } + public function get_google_safety_settings( $settings = null ) { + return $this->get_settings_manager()->get_google_safety_settings( $settings ); + } + + public function get_google_safety_categories() { + return $this->get_settings_manager()->get_google_safety_categories(); + } + + public function get_google_safety_thresholds() { + return $this->get_settings_manager()->get_google_safety_thresholds(); + } + + public function get_loggable_settings_snapshot( $settings = null ) { + return $this->get_settings_manager()->get_loggable_settings_snapshot( $settings ); + } + public function should_use_response_format( Groq_AI_Provider_Interface $provider, $settings ) { return ! $this->is_response_format_compat_enabled( $settings ) && $provider->supports_response_format(); } diff --git a/includes/Admin/class-groq-ai-settings-page.php b/includes/Admin/class-groq-ai-settings-page.php index e7a9c38..2c68cc7 100644 --- a/includes/Admin/class-groq-ai-settings-page.php +++ b/includes/Admin/class-groq-ai-settings-page.php @@ -217,6 +217,9 @@ class Groq_AI_Product_Text_Settings_Page { $google_connected_email = isset( $settings['google_oauth_connected_email'] ) ? (string) $settings['google_oauth_connected_email'] : ''; $google_connected_at = isset( $settings['google_oauth_connected_at'] ) ? absint( $settings['google_oauth_connected_at'] ) : 0; $oauth_redirect = add_query_arg( 'action', 'groq_ai_google_oauth_callback', admin_url( 'admin-post.php' ) ); + $google_safety_settings = $this->plugin->get_google_safety_settings( $settings ); + $google_safety_categories = $this->plugin->get_google_safety_categories(); + $google_safety_thresholds = $this->plugin->get_google_safety_thresholds(); ?>
@@ -281,6 +284,30 @@ class Groq_AI_Product_Text_Settings_Page {

get_label() ) ); ?>

+ +
+ +

+ $info ) : + $category_label = isset( $info['label'] ) ? $info['label'] : $category_key; + $category_description = isset( $info['description'] ) ? $info['description'] : ''; + $selected_threshold = isset( $google_safety_settings[ $category_key ] ) ? $google_safety_settings[ $category_key ] : ''; + $field_id = 'groq-ai-google-safety-' . sanitize_html_class( $category_key ); + ?> + + +
+ @@ -892,6 +919,34 @@ class Groq_AI_Product_Text_Settings_Page {

+ + +

+
+ + + +

+
+
append_response_instructions( $prompt_with_context, $settings ); } + $request_parameters = $this->build_request_parameters_snapshot( + $settings, + [ + 'provider' => $provider_key, + 'conversation_id' => $conversation_id, + 'temperature' => 0.7, + 'response_format_mode' => $use_response_format ? 'structured' : 'prompt', + 'response_format_definition' => $response_format, + 'term_context' => [ + 'term_id' => $term_id, + 'taxonomy' => $taxonomy, + ], + 'term_options' => $usage_meta['term_options'], + 'origin' => $origin, + 'google_safety_settings' => isset( $settings['google_safety_settings'] ) ? $settings['google_safety_settings'] : [], + ] + ); + $model = $this->plugin->get_selected_model( $provider, $settings ); + $request_parameters['model'] = $model; $result = $provider->generate_content( [ 'prompt' => $final_prompt, @@ -212,20 +231,21 @@ class Groq_AI_Ajax_Controller { ); if ( is_wp_error( $result ) ) { - if ( $logger ) { - $logger->log_generation_event( - [ - 'provider' => $provider_key, - 'model' => $model, - 'prompt' => $final_prompt, - 'response' => '', - 'usage' => $usage_meta, - 'status' => 'error', - 'error_message' => $result->get_error_message(), - 'post_id' => 0, - ] - ); - } + if ( $logger ) { + $logger->log_generation_event( + [ + 'provider' => $provider_key, + 'model' => $model, + 'prompt' => $final_prompt, + 'response' => '', + 'usage' => $usage_meta, + 'status' => 'error', + 'error_message' => $result->get_error_message(), + 'post_id' => 0, + 'parameters' => $request_parameters, + ] + ); + } return $result; } @@ -241,20 +261,21 @@ class Groq_AI_Ajax_Controller { $parsed = $prompt_builder->parse_term_structured_response( $response_text, $settings ); } if ( is_wp_error( $parsed ) ) { - if ( $logger ) { - $logger->log_generation_event( - [ - 'provider' => $provider_key, - 'model' => $model, - 'prompt' => $final_prompt, - 'response' => $response_text, - 'usage' => $response_usage, - 'status' => 'error', - 'error_message' => $parsed->get_error_message(), - 'post_id' => 0, - ] - ); - } + if ( $logger ) { + $logger->log_generation_event( + [ + 'provider' => $provider_key, + 'model' => $model, + 'prompt' => $final_prompt, + 'response' => $response_text, + 'usage' => $response_usage, + 'status' => 'error', + 'error_message' => $parsed->get_error_message(), + 'post_id' => 0, + 'parameters' => $request_parameters, + ] + ); + } return $parsed; } if ( ! is_array( $parsed ) ) { @@ -263,19 +284,20 @@ class Groq_AI_Ajax_Controller { ]; } - if ( $logger ) { - $logger->log_generation_event( - [ - 'provider' => $provider_key, - 'model' => $model, - 'prompt' => $final_prompt, - 'response' => $response_text, - 'usage' => $response_usage, - 'status' => 'success', - 'post_id' => 0, - ] - ); - } + if ( $logger ) { + $logger->log_generation_event( + [ + 'provider' => $provider_key, + 'model' => $model, + 'prompt' => $final_prompt, + 'response' => $response_text, + 'usage' => $response_usage, + 'status' => 'success', + 'post_id' => 0, + 'parameters' => $request_parameters, + ] + ); + } return [ 'top_description' => isset( $parsed['top_description'] ) ? $parsed['top_description'] : ( isset( $parsed['description'] ) ? $parsed['description'] : '' ), @@ -492,6 +514,23 @@ class Groq_AI_Ajax_Controller { $final_prompt = $prompt_builder->append_response_instructions( $prompt_with_context, $settings ); } + $request_parameters = $this->build_request_parameters_snapshot( + $settings, + [ + 'provider' => $provider_key, + 'model' => $model, + 'post_id' => $post_id, + 'conversation_id' => $conversation_id, + 'temperature' => 0.7, + 'response_format_mode' => $use_response_format ? 'structured' : 'prompt', + 'response_format_definition' => $response_format, + 'context_fields' => $context_fields, + 'attribute_includes' => isset( $settings['product_attribute_includes'] ) ? $settings['product_attribute_includes'] : [], + 'image_context' => $image_context_meta, + 'google_safety_settings' => isset( $settings['google_safety_settings'] ) ? $settings['google_safety_settings'] : [], + ] + ); + $result = $provider->generate_content( [ 'prompt' => $final_prompt, @@ -518,6 +557,7 @@ class Groq_AI_Ajax_Controller { 'post_id' => $post_id, 'status' => 'error', 'error_message' => $result->get_error_message(), + 'parameters' => $request_parameters, ] ); wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 ); @@ -543,6 +583,7 @@ class Groq_AI_Ajax_Controller { 'post_id' => $post_id, 'status' => 'error', 'error_message' => $response->get_error_message(), + 'parameters' => $request_parameters, ] ); wp_send_json_error( [ 'message' => $response->get_error_message() ], 500 ); @@ -557,6 +598,7 @@ class Groq_AI_Ajax_Controller { 'usage' => $response_usage, 'post_id' => $post_id, 'status' => 'success', + 'parameters' => $request_parameters, ] ); @@ -607,4 +649,16 @@ class Groq_AI_Ajax_Controller { return (string) $result; } + + private function build_request_parameters_snapshot( $settings, array $additional = [] ) { + $snapshot = [ + 'settings' => $this->plugin->get_loggable_settings_snapshot( $settings ), + ]; + + foreach ( $additional as $key => $value ) { + $snapshot[ $key ] = $value; + } + + return $snapshot; + } } diff --git a/includes/Providers/class-groq-ai-provider-google.php b/includes/Providers/class-groq-ai-provider-google.php index ff6465f..49dba52 100644 --- a/includes/Providers/class-groq-ai-provider-google.php +++ b/includes/Providers/class-groq-ai-provider-google.php @@ -30,7 +30,7 @@ class Groq_AI_Provider_Google implements Groq_AI_Provider_Interface { } public function supports_response_format() { - return false; + return true; } public function supports_image_context() { @@ -153,6 +153,18 @@ class Groq_AI_Provider_Google implements Groq_AI_Provider_Interface { } $max_tokens = max( 128, min( 8192, $max_tokens ) ); + $generation_config = [ + 'temperature' => isset( $args['temperature'] ) ? (float) $args['temperature'] : 0.7, + 'maxOutputTokens' => $max_tokens, + ]; + + $response_format = isset( $args['response_format'] ) ? $args['response_format'] : null; + $schema_payload = $this->prepare_response_schema_payload( $response_format ); + if ( ! empty( $schema_payload ) ) { + $generation_config['responseMimeType'] = 'application/json'; + $generation_config['responseJsonSchema'] = $schema_payload; + } + $payload = [ 'contents' => [ [ @@ -160,12 +172,17 @@ class Groq_AI_Provider_Google implements Groq_AI_Provider_Interface { 'parts' => $parts, ], ], - 'generationConfig' => [ - 'temperature' => isset( $args['temperature'] ) ? (float) $args['temperature'] : 0.7, - 'maxOutputTokens' => $max_tokens, - ], + 'generationConfig' => $generation_config, ]; + $safety_settings_payload = $this->build_safety_settings_payload( + isset( $settings['google_safety_settings'] ) ? $settings['google_safety_settings'] : [] + ); + + if ( ! empty( $safety_settings_payload ) ) { + $payload['safetySettings'] = $safety_settings_payload; + } + $response = wp_remote_post( $endpoint, [ @@ -204,7 +221,11 @@ class Groq_AI_Provider_Google implements Groq_AI_Provider_Interface { } $content = trim( implode( "\n\n", array_filter( $texts ) ) ); - $usage = isset( $body['usageMetadata'] ) && is_array( $body['usageMetadata'] ) ? $body['usageMetadata'] : []; + $usage_metadata = isset( $body['usageMetadata'] ) && is_array( $body['usageMetadata'] ) ? $body['usageMetadata'] : []; + $usage = $usage_metadata; + if ( ! empty( $usage_metadata ) ) { + $usage = array_merge( $usage, $this->map_usage_metadata_counts( $usage_metadata ) ); + } $finish_reason = isset( $body['candidates'][0]['finishReason'] ) ? sanitize_text_field( (string) $body['candidates'][0]['finishReason'] ) : ''; if ( '' !== $finish_reason ) { $usage['finish_reason'] = $finish_reason; @@ -216,4 +237,112 @@ class Groq_AI_Provider_Google implements Groq_AI_Provider_Interface { 'raw_response' => $body, ]; } + + private function build_safety_settings_payload( $settings ) { + if ( empty( $settings ) || ! is_array( $settings ) ) { + return []; + } + + $categories = class_exists( 'Groq_AI_Settings_Manager' ) ? array_keys( Groq_AI_Settings_Manager::get_google_safety_categories_list() ) : []; + $thresholds = class_exists( 'Groq_AI_Settings_Manager' ) ? array_keys( Groq_AI_Settings_Manager::get_google_safety_thresholds_list() ) : []; + + if ( empty( $categories ) || empty( $thresholds ) ) { + return []; + } + + $payload = []; + foreach ( $settings as $category => $threshold ) { + $category = sanitize_text_field( (string) $category ); + $threshold = sanitize_text_field( (string) $threshold ); + + if ( ! in_array( $category, $categories, true ) || ! in_array( $threshold, $thresholds, true ) ) { + continue; + } + + $payload[] = [ + 'category' => $category, + 'threshold' => $threshold, + ]; + } + + return $payload; + } + + private function prepare_response_schema_payload( $response_format ) { + if ( empty( $response_format ) || ! is_array( $response_format ) ) { + return []; + } + + if ( isset( $response_format['type'] ) && 'json_schema' === $response_format['type'] ) { + if ( isset( $response_format['json_schema']['schema'] ) && is_array( $response_format['json_schema']['schema'] ) ) { + return $this->sanitize_schema_definition( $response_format['json_schema']['schema'] ); + } + + if ( isset( $response_format['schema'] ) && is_array( $response_format['schema'] ) ) { + return $this->sanitize_schema_definition( $response_format['schema'] ); + } + } + + return []; + } + + private function sanitize_schema_definition( $schema ) { + if ( ! is_array( $schema ) ) { + return []; + } + + $encoded = wp_json_encode( $schema ); + if ( ! $encoded ) { + return []; + } + + $decoded = json_decode( $encoded, true ); + + if ( ! is_array( $decoded ) ) { + return []; + } + + $this->remove_disallowed_schema_keys( $decoded ); + + return $decoded; + } + + private function remove_disallowed_schema_keys( array &$schema ) { + $disallowed = [ 'additionalProperties' ]; + + foreach ( $schema as $key => &$value ) { + if ( in_array( $key, $disallowed, true ) ) { + unset( $schema[ $key ] ); + continue; + } + + if ( is_array( $value ) ) { + $this->remove_disallowed_schema_keys( $value ); + } + } + + unset( $value ); + } + + private function map_usage_metadata_counts( $metadata ) { + if ( ! is_array( $metadata ) ) { + return []; + } + + $mapped = []; + + if ( isset( $metadata['promptTokenCount'] ) ) { + $mapped['prompt_tokens'] = absint( $metadata['promptTokenCount'] ); + } + + if ( isset( $metadata['candidatesTokenCount'] ) ) { + $mapped['completion_tokens'] = absint( $metadata['candidatesTokenCount'] ); + } + + if ( isset( $metadata['totalTokenCount'] ) ) { + $mapped['total_tokens'] = absint( $metadata['totalTokenCount'] ); + } + + return $mapped; + } } diff --git a/includes/Services/Logging/class-groq-ai-generation-logger.php b/includes/Services/Logging/class-groq-ai-generation-logger.php index fc6793a..ffb7a1c 100644 --- a/includes/Services/Logging/class-groq-ai-generation-logger.php +++ b/includes/Services/Logging/class-groq-ai-generation-logger.php @@ -26,9 +26,32 @@ class Groq_AI_Generation_Logger { $table = $this->get_logs_table_name(); $usage = isset( $args['usage'] ) && is_array( $args['usage'] ) ? $args['usage'] : []; - $prompt_tokens = isset( $usage['prompt_tokens'] ) ? absint( $usage['prompt_tokens'] ) : null; - $completion_tokens = isset( $usage['completion_tokens'] ) ? absint( $usage['completion_tokens'] ) : null; - $total_tokens = isset( $usage['total_tokens'] ) ? absint( $usage['total_tokens'] ) : null; + $parameters = isset( $args['parameters'] ) && is_array( $args['parameters'] ) ? $args['parameters'] : []; + $prompt_tokens = $this->extract_usage_token_value( + $usage, + [ + 'prompt_tokens', + 'promptTokenCount', + 'input_tokens', + 'inputTokenCount', + ] + ); + $completion_tokens = $this->extract_usage_token_value( + $usage, + [ + 'completion_tokens', + 'output_tokens', + 'candidatesTokenCount', + 'outputTokenCount', + ] + ); + $total_tokens = $this->extract_usage_token_value( + $usage, + [ + 'total_tokens', + 'totalTokenCount', + ] + ); $wpdb->insert( $table, @@ -46,6 +69,7 @@ class Groq_AI_Generation_Logger { 'status' => isset( $args['status'] ) ? sanitize_text_field( $args['status'] ) : 'success', 'error_message' => isset( $args['error_message'] ) ? $args['error_message'] : '', 'usage_json' => ! empty( $usage ) ? wp_json_encode( $usage ) : null, + 'request_json' => ! empty( $parameters ) ? wp_json_encode( $parameters ) : null, ] ); } @@ -77,6 +101,7 @@ class Groq_AI_Generation_Logger { public function maybe_create_table() { if ( get_option( self::OPTION_TABLE_CREATED ) ) { $this->logs_table_exists = true; + $this->maybe_upgrade_table_schema(); return; } @@ -106,6 +131,7 @@ class Groq_AI_Generation_Logger { status varchar(20) NOT NULL, error_message text DEFAULT NULL, usage_json longtext DEFAULT NULL, + request_json longtext DEFAULT NULL, PRIMARY KEY (id), KEY provider (provider), KEY post_id (post_id) @@ -117,6 +143,26 @@ class Groq_AI_Generation_Logger { update_option( self::OPTION_TABLE_CREATED, 1 ); } + private function extract_usage_token_value( $usage, $keys ) { + foreach ( (array) $keys as $key ) { + if ( isset( $usage[ $key ] ) ) { + return absint( $usage[ $key ] ); + } + } + + return null; + } + + private function maybe_upgrade_table_schema() { + global $wpdb; + + $table = $this->get_logs_table_name(); + $column = $wpdb->get_var( $wpdb->prepare( "SHOW COLUMNS FROM {$table} LIKE %s", 'request_json' ) ); + if ( ! $column ) { + $this->create_table(); + } + } + private function get_logs_table_name() { global $wpdb; diff --git a/includes/Services/Settings/class-groq-ai-settings-manager.php b/includes/Services/Settings/class-groq-ai-settings-manager.php index 82a3816..20eed72 100644 --- a/includes/Services/Settings/class-groq-ai-settings-manager.php +++ b/includes/Services/Settings/class-groq-ai-settings-manager.php @@ -47,6 +47,7 @@ class Groq_AI_Settings_Manager { 'google_enable_ga' => true, 'google_gsc_site_url' => '', 'google_ga4_property_id' => '', + 'google_safety_settings' => [], 'context_fields' => $this->get_default_context_fields(), 'modules' => $this->get_default_modules_settings(), 'image_context_mode' => 'url', @@ -60,6 +61,7 @@ class Groq_AI_Settings_Manager { $settings = wp_parse_args( (array) $settings, $defaults ); $settings['context_fields'] = $this->normalize_context_fields( isset( $settings['context_fields'] ) ? $settings['context_fields'] : [] ); $settings['modules'] = $this->sanitize_modules_settings( isset( $settings['modules'] ) ? $settings['modules'] : [] ); + $settings['google_safety_settings'] = $this->sanitize_google_safety_settings( isset( $settings['google_safety_settings'] ) ? $settings['google_safety_settings'] : [] ); $settings['model'] = Groq_AI_Model_Exclusions::ensure_allowed( $settings['provider'], isset( $settings['model'] ) ? $settings['model'] : '' ); $image_mode = isset( $settings['image_context_mode'] ) ? sanitize_text_field( $settings['image_context_mode'] ) : 'url'; @@ -120,6 +122,7 @@ class Groq_AI_Settings_Manager { 'google_enable_ga' => true, 'google_gsc_site_url' => '', 'google_ga4_property_id' => '', + 'google_safety_settings' => [], 'context_fields' => $this->get_default_context_fields(), 'modules' => $this->get_default_modules_settings(), 'image_context_mode' => 'url', @@ -193,6 +196,7 @@ class Groq_AI_Settings_Manager { 'google_enable_ga' => ! empty( $raw_input['google_enable_ga'] ), 'google_gsc_site_url' => esc_url_raw( (string) $input['google_gsc_site_url'] ), 'google_ga4_property_id' => sanitize_text_field( (string) $input['google_ga4_property_id'] ), + 'google_safety_settings' => $this->sanitize_google_safety_settings( isset( $raw_input['google_safety_settings'] ) ? $raw_input['google_safety_settings'] : [] ), 'response_format_compat' => ! empty( $raw_input['response_format_compat'] ), 'image_context_mode' => $image_mode, 'image_context_limit' => $image_limit, @@ -422,6 +426,94 @@ class Groq_AI_Settings_Manager { return $this->sanitize_term_description_char_limit_value( $value, 1200 ); } + public function get_google_safety_settings( $settings = null ) { + if ( null === $settings ) { + $settings = $this->all(); + } + + return $this->sanitize_google_safety_settings( isset( $settings['google_safety_settings'] ) ? $settings['google_safety_settings'] : [] ); + } + + public function get_google_safety_categories() { + return self::get_google_safety_categories_list(); + } + + public function get_google_safety_thresholds() { + return self::get_google_safety_thresholds_list(); + } + + public function get_loggable_settings_snapshot( $settings = null ) { + if ( null === $settings ) { + $settings = $this->all(); + } + + $allowed_keys = [ + 'store_context', + 'default_prompt', + 'max_output_tokens', + 'product_attribute_includes', + 'context_fields', + 'modules', + 'image_context_mode', + 'image_context_limit', + 'response_format_compat', + 'term_top_description_char_limit', + 'term_bottom_description_char_limit', + 'term_bottom_description_meta_key', + 'google_safety_settings', + 'google_enable_gsc', + 'google_enable_ga', + 'google_gsc_site_url', + 'google_ga4_property_id', + ]; + + $snapshot = []; + + foreach ( $allowed_keys as $key ) { + if ( array_key_exists( $key, $settings ) ) { + $snapshot[ $key ] = $settings[ $key ]; + } + } + + return $snapshot; + } + + public static function get_google_safety_categories_list() { + return [ + 'HARM_CATEGORY_HARASSMENT' => [ + 'label' => __( 'Harassment & intimidatie', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Detecteert bedreigingen en pesterijen in de output.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ], + 'HARM_CATEGORY_HATE_SPEECH' => [ + 'label' => __( 'Haatspraak', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Beperkt discriminerende of denigrerende taal.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ], + 'HARM_CATEGORY_SEXUALLY_EXPLICIT' => [ + 'label' => __( 'Seksueel expliciet', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Filtert beschrijvingen van seksuele handelingen of fetish-content.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ], + 'HARM_CATEGORY_DANGEROUS_CONTENT' => [ + 'label' => __( 'Gevaarlijke activiteiten', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Voorkomt instructies rond geweld, wapens of gevaarlijke middelen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ], + 'HARM_CATEGORY_CIVIC_INTEGRITY' => [ + 'label' => __( 'Civieke integriteit', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Vermindert desinformatie rond verkiezingen en burgerprocessen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ], + ]; + } + + public static function get_google_safety_thresholds_list() { + return [ + '' => __( 'Google standaard (niet meesturen)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' => __( 'Onbekende drempel (laat Google beslissen)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'BLOCK_LOW_AND_ABOVE' => __( 'Blokkeer lage ernst en hoger', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'BLOCK_MEDIUM_AND_ABOVE' => __( 'Blokkeer middel en hoger', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'BLOCK_ONLY_HIGH' => __( 'Blokkeer alleen hoge ernst', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'BLOCK_NONE' => __( 'Sta alles toe (geen blokkade)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + ]; + } + public function is_response_format_compat_enabled( $settings = null ) { if ( null === $settings ) { $settings = $this->all(); @@ -505,4 +597,27 @@ class Groq_AI_Settings_Manager { return min( 10, $limit ); } + + private function sanitize_google_safety_settings( $settings ) { + if ( ! is_array( $settings ) ) { + return []; + } + + $categories = array_keys( self::get_google_safety_categories_list() ); + $thresholds = array_keys( self::get_google_safety_thresholds_list() ); + $clean = []; + + foreach ( $settings as $category => $threshold ) { + $category = sanitize_text_field( (string) $category ); + $threshold = sanitize_text_field( (string) $threshold ); + + if ( '' === $threshold || ! in_array( $category, $categories, true ) || ! in_array( $threshold, $thresholds, true ) ) { + continue; + } + + $clean[ $category ] = $threshold; + } + + return $clean; + } }