PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / trunk
Kubio AI Page Builder vtrunk
2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / lib / AI / utils.php
kubio / lib / AI Last commit date
Blog 9 months ago SDApi 1 year ago Shop 9 months ago admin-tab 1 year ago filters 1 year ago PostImage.php 1 year ago api.php 9 months ago index.php 1 year ago utils.php 1 year ago
utils.php
515 lines
1 <?php
2
3 use IlluminateAgnostic\Arr\Support\Arr;
4 use Kubio\Core\LodashBasic;
5 use Kubio\Core\Utils;
6 use Kubio\FileLog;
7 use Kubio\Flags;
8
9 function kubio_ai_content_languages() {
10 return array(
11 'ar_AR' => 'العربية (Arabic)',
12 'az_AZ' => 'Azərbaycan dili (Azerbaijani)',
13 'bn_BD' => 'বাংলা (Bengali)',
14 'cs_CZ' => 'Čeština (Czech)',
15 'cy_GB' => 'Cymraeg (Welsh)',
16 'da_DK' => 'Dansk (Danish)',
17 'de_DE' => 'Deutsch (German)',
18 'el_GR' => 'Ελληνικά (Greek)',
19 'en_US' => 'English US',
20 'en_GB' => 'English GB (United Kingdom English)',
21 'en_AU' => 'English AU (Australian English)',
22 'en_CA' => 'English CA (Canadian English)',
23 'es_ES' => 'Español (Spanish)',
24 'es_MX' => 'Español MX (Mexican Spanish)',
25 'et_EE' => 'Eesti keel (Estonian)',
26 'fa_IR' => 'فارسی (Persian)',
27 'fi_FI' => 'Suomi (Finnish)',
28 'fr_FR' => 'Français (French)',
29 'fr_CA' => 'Français CA (Canadian French)',
30 'ga_IE' => 'Gaeilge (Irish)',
31 'he_IL' => 'עברית (Hebrew)',
32 'hi_IN' => 'हिन्दी (Hindi)',
33 'hr_HR' => 'Hrvatski (Croatian)',
34 'hu_HU' => 'Magyar (Hungarian)',
35 'hy_AM' => 'Հայերեն (Armenian)',
36 'id_ID' => 'Bahasa Indonesia (Indonesian)',
37 'is_IS' => 'Íslenska (Icelandic)',
38 'it_IT' => 'Italiano (Italian)',
39 'ja_JP' => '日本語 (Japanese)',
40 'ka_GE' => 'ქართული (Georgian)',
41 'kk_KZ' => 'Қазақ тілі (Kazakh)',
42 'ko_KR' => '한국어 (Korean)',
43 'lt_LT' => 'Lietuvių kalba (Lithuanian)',
44 'lv_LV' => 'Latviešu valoda (Latvian)',
45 'ms_MY' => 'Bahasa Melayu (Malay)',
46 'nb_NO' => 'Norsk bokmål (Norwegian Bokmål)',
47 'nl_NL' => 'Nederlands (Dutch)',
48 'pl_PL' => 'Polski (Polish)',
49 'pt_PT' => 'Português (Portuguese)',
50 'pt_BR' => 'Português BR (Portuguese Brasil)',
51 'ro_RO' => 'Română (Romanian)',
52 'ru_RU' => 'Русский (Russian)',
53 'sk_SK' => 'Slovenčina (Slovak)',
54 'sl_SI' => 'Slovenščina (Slovenian)',
55 'sq_AL' => 'Shqip (Albanian)',
56 'sr_RS' => 'Српски (Serbian)',
57 'sv_SE' => 'Svenska (Swedish)',
58 'ta_IN' => 'தமிழ் (Tamil)',
59 'th_TH' => 'ไทย (Thai)',
60 'tr_TR' => 'Türkçe (Turkish)',
61 'uk_UA' => 'Українська (Ukrainian)',
62 'ur_PK' => 'اردو (Urdu)',
63 'vi_VN' => 'Tiếng Việt (Vietnamese)',
64 'zh_CN' => '中文 (Chinese Simplified)',
65 'zh_TW' => '中文 (Chinese Traditional)',
66 );
67 }
68
69
70 function kubio_ai_content_language_styles() {
71 return array(
72 'natural' => array(
73 'label' => __( 'Natural', 'kubio' ),
74 'description' => __( 'Is the default language style. It can adapt to some extent depending on the cues from the user\'s input.', 'kubio' ),
75 ),
76 'formal' => array(
77 'label' => __( 'Formal', 'kubio' ),
78 'description' => __( 'Used in official documents, academic papers, and professional communication, characterized by complex sentence structures, precise vocabulary, and avoidance of contractions and slang.', 'kubio' ),
79 ),
80 'informal' => array(
81 'label' => __( 'Informal', 'kubio' ),
82 'description' => __( 'Casual and relaxed style used in everyday conversations, personal emails, and informal writing. It often includes contractions, colloquialisms, and a more relaxed sentence structure.', 'kubio' ),
83 ),
84 'technical' => array(
85 'label' => __( 'Technical', 'kubio' ),
86 'description' => __( 'Specific to a particular field or industry, this style uses terminology and jargon understood by experts in that field. It aims for clear and precise communication within that domain.', 'kubio' ),
87 ),
88
89 'persuasive' => array(
90 'label' => __( 'Persuasive', 'kubio' ),
91 'description' => __( "Used to convince or influence the reader's opinion, often employing rhetoric, emotional appeals, and strong arguments to present a particular viewpoint.", 'kubio' ),
92 ),
93 'humorous' => array(
94 'label' => __( 'Humorous', 'kubio' ),
95 'description' => __( 'Intended to amuse the reader, this style uses wit, sarcasm, irony, and clever wordplay to evoke laughter or amusement.', 'kubio' ),
96 ),
97
98 );
99 }
100
101 function kubio_ai_business_types() {
102 return array(
103 array(
104 'label' => __( 'Business', 'kubio' ),
105 'value' => 'Business',
106 'description' => __( 'Resources and insights for starting, managing, and growing businesses.', 'kubio' ),
107 ),
108 array(
109 'label' => __( 'Services', 'kubio' ),
110 'value' => 'Services',
111 'description' => __( 'Promoting and detailing services offered by professionals or businesses.', 'kubio' ),
112 ),
113 array(
114 'label' => __( 'Consulting', 'kubio' ),
115 'value' => 'Consulting',
116 'description' => __( 'Offering expert advice, insights, and solutions in specialized fields.', 'kubio' ),
117 ),
118
119 array(
120 'label' => __( 'Art / Design Portfolio', 'kubio' ),
121 'value' => 'Art / Design Portfolio',
122 'description' => __( 'Showcasing artistic and design work, including portfolios, projects, and creative endeavors.', 'kubio' ),
123 ),
124
125 array(
126 'label' => __( 'Community & Non-profit', 'kubio' ),
127 'value' => 'Community & Non-profit',
128 'description' => __( 'Highlighting community initiatives, nonprofit organizations, and philanthropic efforts for social good.', 'kubio' ),
129 ),
130 array(
131 'label' => __( 'Personal', 'kubio' ),
132 'value' => 'Personal',
133 'description' => __( 'A platform for sharing personal experiences, thoughts, and interests.', 'kubio' ),
134 ),
135 array(
136 'label' => __( 'Event', 'kubio' ),
137 'value' => 'Event',
138 'description' => __( 'Information and details about upcoming events, conferences, or gatherings.', 'kubio' ),
139 ),
140
141 array(
142 'label' => __( 'Educational', 'kubio' ),
143 'value' => 'Educational',
144 'description' => __( 'Online presence for educational institutions, showcasing courses, programs, and achievements.', 'kubio' ),
145 ),
146
147 array(
148 'label' => __( 'Food & Drink', 'kubio' ),
149 'value' => 'Food & Drink',
150 'description' => __( 'Exploring culinary delights, recipes, and beverage-related content.', 'kubio' ),
151 ),
152
153 array(
154 'label' => __( 'Health & Fitness', 'kubio' ),
155 'value' => 'Health & Fitness',
156 'description' => __( 'Promoting a healthy lifestyle, fitness routines, and well-being tips.', 'kubio' ),
157 ),
158
159 array(
160 'label' => __( 'Fashion & Beauty', 'kubio' ),
161 'value' => 'Fashion & Beauty',
162 'description' => __( 'Showcasing the latest fashion trends, beauty tips, and style advice.', 'kubio' ),
163 ),
164
165 array(
166 'label' => __( 'Technology & Gadgets', 'kubio' ),
167 'value' => 'Technology & Gadgets',
168 'description' => __( 'Exploring the world of tech, gadgets, and digital innovations.', 'kubio' ),
169 ),
170
171 array(
172 'label' => __( 'Gaming', 'kubio' ),
173 'value' => 'Gaming',
174 'description' => __( 'Exploring the world of gaming, video games, and esports.', 'kubio' ),
175 ),
176
177 );
178 }
179
180 function kubio_ai_log( $type, $code, $prompts, $completion, $usage = array(), $call_id = null ) {
181
182 if ( defined( 'KUBIO_AI_LOG' ) && KUBIO_AI_LOG ) {
183 kubio_replace_base64_strings( $completion );
184
185 return FileLog::with_type( FileLog::JSONL_LOG )->$type(
186 'AI',
187 array(
188 'call_id' => $call_id,
189 'code' => $code,
190 'prompts' => $prompts,
191 'completion' => $completion,
192 'start_time' => Arr::get( $usage, 'start_time', 0 ),
193 'usage' => array_merge(
194 array(
195 'prompt_tokens' => 0,
196 'completion_tokens' => 0,
197 'total_tokens' => 0,
198 ),
199 $usage
200 ),
201 )
202 );
203 }
204 }
205
206 function kubio_replace_base64_strings( &$array, $max_length = 500, $replace_with = '[blob]' ) {
207 if ( ! is_array(
208 $array
209 ) ) {
210 return false;
211 }
212
213 foreach ( $array as $key => &$value ) {
214 if ( is_array( $value ) ) {
215 kubio_replace_base64_strings(
216 $value,
217 $max_length,
218 $replace_with
219 );
220 } elseif ( is_string( $value ) && strlen( $value ) > $max_length && kubio_is_base64( $value ) ) {
221 $array[ $key ] = $replace_with;
222 }
223 }
224 }
225
226 function kubio_is_base64( $string ) {
227 return base64_decode( $string, true ) !== false;
228 }
229
230 function kubio_ai_sd_xl_determine_appropriate_size( $width, $height ) {
231
232 $ar_range = 0.2;
233 $aspect_ratio = $width / $height;
234
235 $sizes = array(
236 640 => 1536,
237 768 => 1344,
238 832 => 1216,
239 896 => 1152,
240 1024 => 1024,
241 1152 => 896,
242 1216 => 832,
243 );
244
245 $heights_in_range = array();
246 foreach ( $sizes as $possible_height => $possible_width ) {
247 $possible_ar = $possible_width / $possible_height;
248 $ar_is_in_range = $aspect_ratio * ( 1 - $ar_range ) <= $possible_ar && $possible_ar <= $aspect_ratio * ( 1 + $ar_range );
249
250 if ( $ar_is_in_range ) {
251 $heights_in_range[] = $possible_height;
252 }
253 }
254
255 $final_height = empty( $heights_in_range ) ? 1024 : array_shift( $heights_in_range );
256
257 foreach ( $heights_in_range as $possible_height ) {
258 if ( $possible_height > $height ) {
259 break;
260 }
261
262 $final_height = $height;
263 }
264
265 return array( $sizes[ $final_height ], $final_height );
266 }
267
268 function kubio_ai_get_original_image_dimensions( $imag_url ) {
269
270 if ( ! function_exists( 'download_url' ) ) {
271 require_once ABSPATH . '/wp-admin/includes/file.php';
272 }
273
274 $temp_file = download_url( $imag_url );
275 $image_threshold = 0.2;
276
277 if ( is_wp_error( $temp_file ) ) {
278 return array(
279 'width' => 1920,
280 'height' => 1080,
281 'orientation' => 'landscape',
282 );
283 }
284
285 $image_size = getimagesize( $temp_file );
286
287 if ( $image_size === false ) {
288
289 return array(
290 'width' => 1920,
291 'height' => 1080,
292 'orientation' => 'landscape',
293 );
294 }
295
296 list($width, $height) = $image_size;
297
298 wp_delete_file( $temp_file );
299
300 $ratio = floor( $width / $height * 100 ) / 100;
301 $orientation = $ratio < 1 ? 'portrait' : 'landscape';
302
303 if ( ( 1 - $image_threshold ) < $ratio && $ratio < ( 1 + $image_threshold ) ) {
304 $orientation = 'square';
305 }
306
307 return array(
308 'width' => $width,
309 'height' => $height,
310 'orientation' => $orientation,
311 );
312 }
313
314 /**
315 *
316 * @param string $path
317 * @param array $payload
318 * @param array $extra
319 * @return mixed|null|WP_Error
320 */
321 function kubio_ai_call_api( $path, $payload = array(), $extra = array() ) {
322
323 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
324 $is_guest = isset( $_GET['is_guest'] ) && ! ! $_GET['is_guest'];
325 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
326 $skip_ai_cache = isset( $_GET['skip_ai_cache'] ) && ! ! $_GET['skip_ai_cache'];
327 $timeout = 120;
328
329 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
330 ini_set( 'max_execution_time', $timeout );
331 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
332 set_time_limit( $timeout );
333
334 $log = defined( 'KUBIO_AI_LOG' ) ? KUBIO_AI_LOG : false;
335 if ( ! $is_guest ) {
336 $base_url = Utils::getCloudURL( "/api/ai/$path" );
337 } else {
338 $base_url = Utils::getCloudURL( "/api/ai-guest/$path" );
339 }
340
341 $url = add_query_arg(
342 array(
343 'ai_debug' => $log ? 1 : 0,
344 ),
345 $base_url
346 );
347
348 $start_time = microtime( true );
349 $filter = "kubio_ai_response_{$path}";
350 // add a filter
351 if ( has_filter( $filter ) ) {
352 $response = apply_filters( $filter, $payload );
353
354 return $response;
355 }
356 $extra_headers = array();
357 if ( $skip_ai_cache ) {
358 $extra_headers['X-Kubio-AI-Skip-Cache'] = '1';
359 }
360 $response = wp_remote_post(
361 $url,
362 array(
363 'timeout' => $timeout - 1,
364 'sslverify' => false,
365 'headers' => array_merge(
366 array(
367 'Content-Type' => 'application/json',
368 'Accept' => 'application/json',
369 'X-Kubio-AI-Key' => kubio_ai_get_key(),
370 'X-Kubio-Site-UUID' => Flags::getSiteUUID(),
371 'X-Kubio-Site-URL' => get_bloginfo( 'url' ),
372 ),
373 $extra_headers
374 ),
375 'body' => json_encode(
376 array_merge(
377 array(
378 'params' => $payload,
379 ),
380 $extra
381 )
382 ),
383 )
384 );
385
386 if ( is_wp_error( $response ) ) {
387 return $response;
388 }
389
390 $code = wp_remote_retrieve_response_code( $response );
391
392 $result = null;
393 $debug_content = array();
394 $response_content = null;
395
396 if ( $code !== 200 ) {
397 $response_content = wp_remote_retrieve_body( $response );
398 $body = json_decode( $response_content, true );
399 $error = Arr::get( (array) $body, 'error', null );
400 if ( $error ) {
401 $result = new \WP_Error(
402 Arr::get( $error, 'code', '' ),
403 Arr::get( $error, 'message', '' ),
404 $response_content
405 );
406 } else {
407 $result = new \WP_Error(
408 $code,
409 // translators: %s is the error message
410 sprintf( __( 'AI Server error: %s', 'kubio' ), Arr::get( $error, 'message', '' ) ),
411 $response_content
412 );
413 }
414 } else {
415 $response_content = wp_remote_retrieve_body( $response );
416 $body = json_decode( $response_content, true );
417
418 $result = array(
419 'content' => Arr::get( is_array( $body ) ? $body : array(), 'content', null ),
420 'service_info' => (object) Arr::get( $body, 'service_info', array() ),
421 );
422
423 if ( ! is_array( $body ) ) {
424 $result = new WP_Error(
425 'ai_response_not_json',
426 __( 'AI response is invalid', 'kubio' ),
427 $response_content
428 );
429 } else {
430
431 $error = $body['error'];
432 $debug_content = Arr::get( $body, 'debug', array() );
433
434 if ( $error ) {
435 $result = new WP_Error(
436 $error['code'],
437 $error['message'],
438 array_merge(
439 array( 'debug' => LodashBasic::omit( $debug_content, array( 'messages' ) ) ),
440 $result
441 )
442 );
443 }
444 }
445 }
446
447 if ( $log ) {
448 $messages = array_merge(
449 array(
450 array(
451 'role' => 'api payload',
452 'content' => json_encode( $payload, JSON_PRETTY_PRINT ),
453 ),
454 ),
455 Arr::get(
456 $debug_content,
457 'messages',
458 array()
459 )
460 );
461
462 $log_response = Arr::get( $debug_content, 'response_raw', array() );
463
464 if ( is_wp_error( $result ) ) {
465
466 $response_messages = array();
467 foreach ( $result->get_all_error_data() as $error_data ) {
468 $error_data = Utils::maybeJSONDecode( $error_data );
469 if ( is_array( $error_data ) ) {
470 $response_messages[] = Arr::get( $error_data, 'debug', $error_data );
471 } else {
472 $response_messages[] = $error_data;
473 }
474 }
475
476 $log_response = array(
477 'error_codes' => $result->get_error_codes(),
478 'error_messages' => $result->get_error_messages(),
479 'response_messages' => $response_messages,
480 );
481 }
482
483 if ( $result === null ) {
484 $result = new WP_Error( 'ai_response_not_json', __( 'AI response is invalid ( Empty response )', 'kubio' ) );
485 $log_response = wp_remote_retrieve_body( $response );
486 }
487
488 $log_code = $path;
489 if ( strpos( $path, '/generate-page-section' ) !== false || strpos( $path, '/rephrase-page-section' ) !== false ) {
490 $log_code = sprintf( '%s ( %s )', $log_code, Arr::get( $payload, 'category', 'N/A' ) );
491 }
492
493 kubio_ai_log(
494 is_wp_error( $result ) ? 'error' : 'info',
495 $log_code,
496 $messages,
497 $log_response,
498 array(
499 'start_time' => $start_time,
500 'total_tokens' => Arr::get( $debug_content, 'total_tokens', 0 ),
501 'prompt_tokens' => Arr::get( $debug_content, 'prompt_tokens', 0 ),
502 'completion_tokens' => Arr::get( $debug_content, 'completion_tokens', 0 ),
503 ),
504 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
505 sanitize_text_field( Arr::get( $_REQUEST, '__kubio_call_id', null ) )
506 );
507 }
508
509 if ( is_array( $result ) ) {
510 $result['upgrade_key'] = sprintf( 'ai:%s', apply_filters( 'kubio/ai/upgrade-key', base64_encode( kubio_ai_get_key() ) ) );
511 }
512
513 return $result;
514 }
515