PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.96
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.96
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / core.php
ai-engine / classes Last commit date
modules 3 years ago admin.php 3 years ago ai.php 3 years ago answer.php 3 years ago api.php 3 years ago core.php 3 years ago init.php 3 years ago openai.php 3 years ago query.php 3 years ago queryembed.php 3 years ago queryimage.php 3 years ago querytext.php 3 years ago querytranscribe.php 3 years ago rest.php 3 years ago security.php 3 years ago ui.php 3 years ago
core.php
592 lines
1 <?php
2
3 require_once( MWAI_PATH . '/vendor/autoload.php' );
4 require_once( MWAI_PATH . '/constants/init.php' );
5
6 use Rahul900day\Gpt3Encoder\Encoder;
7
8 define( 'MWAI_IMG_WAND', MWAI_URL . '/images/wand.png' );
9 define( 'MWAI_IMG_WAND_HTML', "<img style='height: 22px; margin-bottom: -5px; margin-right: 8px;'
10 src='" . MWAI_IMG_WAND . "' alt='AI Wand' />" );
11 define( 'MWAI_IMG_WAND_HTML_XS', "<img style='height: 16px; margin-bottom: -2px;'
12 src='" . MWAI_IMG_WAND . "' alt='AI Wand' />" );
13 class Meow_MWAI_Core
14 {
15 public $admin = null;
16 public $is_rest = false;
17 public $is_cli = false;
18 public $site_url = null;
19 public $ai = null;
20 private $option_name = 'mwai_options';
21 public $defaultChatbotParams = MWAI_CHATBOT_PARAMS;
22
23 public function __construct() {
24 $this->site_url = get_site_url();
25 $this->is_rest = MeowCommon_Helpers::is_rest();
26 $this->is_cli = defined( 'WP_CLI' ) && WP_CLI;
27 $this->ai = new Meow_MWAI_AI( $this );
28 add_action( 'plugins_loaded', array( $this, 'init' ) );
29 }
30
31 function init() {
32 global $mwai;
33 $mwai = new Meow_MWAI_API();
34 new Meow_MWAI_Security( $this );
35 if ( $this->is_rest ) {
36 new Meow_MWAI_Rest( $this );
37 }
38 if ( is_admin() ) {
39 new Meow_MWAI_Admin( $this );
40 new Meow_MWAI_Modules_Assistants( $this );
41 }
42 else {
43 //new Meow_MWAI_UI( $this );
44 if ( $this->get_option( 'shortcode_chat' ) ) {
45 new Meow_MWAI_Modules_Chatbot();
46 }
47 }
48
49 // Advanced core
50 if ( class_exists( 'MeowPro_MWAI_Core' ) ) {
51 new MeowPro_MWAI_Core( $this );
52 }
53
54 // Dynamic max tokens
55 if ( $this->get_option( 'dynamic_max_tokens' ) ) {
56 add_filter( 'mwai_estimate_tokens', array( $this, 'dynamic_max_tokens' ), 10, 2 );
57 }
58 }
59
60 #region Roles & Capabilities
61
62 function can_access_settings() {
63 return apply_filters( 'mwai_allow_setup', current_user_can( 'manage_options' ) );
64 }
65
66 function can_access_features() {
67 $editor_or_admin = current_user_can( 'editor' ) || current_user_can( 'administrator' );
68 return apply_filters( 'mwai_allow_usage', $editor_or_admin );
69 }
70
71 #endregion
72
73 #region Text-Related Helpers
74
75 // Clean the text perfectly, resolve shortcodes, etc, etc.
76 function cleanText( $rawText = "" ) {
77 $text = html_entity_decode( $rawText );
78 $text = wp_strip_all_tags( $text );
79 $text = preg_replace( '/[\r\n]+/', "\n", $text );
80 return $text . " ";
81
82 // Before simplification:
83 // $text = strip_tags( $rawText );
84 // $text = strip_shortcodes( $text );
85 // $text = html_entity_decode( $text );
86 // $text = preg_replace( '/[\r\n]+/', "\n", $text );
87 // $sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
88 // foreach ( $sentences as $key => $sentence ) {
89 // $sentences[$key] = trim( $sentence );
90 // }
91 // $text = implode( " ", $sentences );
92 // $text = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $text );
93 // return $text . " ";
94 }
95
96 // Make sure there are no duplicate sentences, and keep the length under a maximum length.
97 function cleanSentences( $text, $maxTokens = 512 ) {
98 //$sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
99 $sentences = preg_split('/(?<=[.?!。.!?])+/u', $text);
100 $hashes = array();
101 $uniqueSentences = array();
102 $length = 0;
103 foreach ( $sentences as $sentence ) {
104 $sentence = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $sentence );
105 $hash = md5( $sentence );
106 if ( !in_array( $hash, $hashes ) ) {
107 $tokensCount = apply_filters( 'mwai_estimate_tokens', 0, $sentence );
108 if ( $length + $tokensCount > $maxTokens ) {
109 continue;
110 }
111 $hashes[] = $hash;
112 $uniqueSentences[] = $sentence;
113 $length += $tokensCount;
114 }
115 }
116 $freshText = implode( " ", $uniqueSentences );
117 $freshText = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $freshText );
118 return $freshText;
119 }
120
121 function getCleanPostContent( $postId ) {
122 $post = get_post( $postId );
123 if ( !$post ) {
124 return false;
125 }
126 $post->post_content = apply_filters( 'the_content', $post->post_content );
127 $text = $this->cleanText( $post->post_content );
128 $text = $this->cleanSentences( $text );
129 return $text;
130 }
131
132 function markdown_to_html( $content ) {
133 $Parsedown = new Parsedown();
134 $content = $Parsedown->text( $content );
135 return $content;
136 }
137
138 function get_post_language( $postId ) {
139 $locale = get_locale();
140 $code = strtolower( substr( $locale, 0, 2 ) );
141 $lang_codes = array(
142 'aa' => 'Afar',
143 'ab' => 'Abkhazian',
144 'af' => 'Afrikaans',
145 'ak' => 'Akan',
146 'sq' => 'Albanian',
147 'am' => 'Amharic',
148 'ar' => 'Arabic',
149 'an' => 'Aragonese',
150 'hy' => 'Armenian',
151 'as' => 'Assamese',
152 'av' => 'Avaric',
153 'ae' => 'Avestan',
154 'ay' => 'Aymara',
155 'az' => 'Azerbaijani',
156 'ba' => 'Bashkir',
157 'bm' => 'Bambara',
158 'eu' => 'Basque',
159 'be' => 'Belarusian',
160 'bn' => 'Bengali',
161 'bh' => 'Bihari',
162 'bi' => 'Bislama',
163 'bs' => 'Bosnian',
164 'br' => 'Breton',
165 'bg' => 'Bulgarian',
166 'my' => 'Burmese',
167 'ca' => 'Catalan; Valencian',
168 'ch' => 'Chamorro',
169 'ce' => 'Chechen',
170 'zh' => 'Chinese',
171 'cu' => 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic',
172 'cv' => 'Chuvash',
173 'kw' => 'Cornish',
174 'co' => 'Corsican',
175 'cr' => 'Cree',
176 'cs' => 'Czech',
177 'da' => 'Danish',
178 'dv' => 'Divehi; Dhivehi; Maldivian',
179 'nl' => 'Dutch; Flemish',
180 'dz' => 'Dzongkha',
181 'en' => 'English',
182 'eo' => 'Esperanto',
183 'et' => 'Estonian',
184 'ee' => 'Ewe',
185 'fo' => 'Faroese',
186 'fj' => 'Fijjian',
187 'fi' => 'Finnish',
188 'fr' => 'French',
189 'fy' => 'Western Frisian',
190 'ff' => 'Fulah',
191 'ka' => 'Georgian',
192 'de' => 'German',
193 'gd' => 'Gaelic; Scottish Gaelic',
194 'ga' => 'Irish',
195 'gl' => 'Galician',
196 'gv' => 'Manx',
197 'el' => 'Greek, Modern',
198 'gn' => 'Guarani',
199 'gu' => 'Gujarati',
200 'ht' => 'Haitian; Haitian Creole',
201 'ha' => 'Hausa',
202 'he' => 'Hebrew',
203 'hz' => 'Herero',
204 'hi' => 'Hindi',
205 'ho' => 'Hiri Motu',
206 'hu' => 'Hungarian',
207 'ig' => 'Igbo',
208 'is' => 'Icelandic',
209 'io' => 'Ido',
210 'ii' => 'Sichuan Yi',
211 'iu' => 'Inuktitut',
212 'ie' => 'Interlingue',
213 'ia' => 'Interlingua (International Auxiliary Language Association)',
214 'id' => 'Indonesian',
215 'ik' => 'Inupiaq',
216 'it' => 'Italian',
217 'jv' => 'Javanese',
218 'ja' => 'Japanese',
219 'kl' => 'Kalaallisut; Greenlandic',
220 'kn' => 'Kannada',
221 'ks' => 'Kashmiri',
222 'kr' => 'Kanuri',
223 'kk' => 'Kazakh',
224 'km' => 'Central Khmer',
225 'ki' => 'Kikuyu; Gikuyu',
226 'rw' => 'Kinyarwanda',
227 'ky' => 'Kirghiz; Kyrgyz',
228 'kv' => 'Komi',
229 'kg' => 'Kongo',
230 'ko' => 'Korean',
231 'kj' => 'Kuanyama; Kwanyama',
232 'ku' => 'Kurdish',
233 'lo' => 'Lao',
234 'la' => 'Latin',
235 'lv' => 'Latvian',
236 'li' => 'Limburgan; Limburger; Limburgish',
237 'ln' => 'Lingala',
238 'lt' => 'Lithuanian',
239 'lb' => 'Luxembourgish; Letzeburgesch',
240 'lu' => 'Luba-Katanga',
241 'lg' => 'Ganda',
242 'mk' => 'Macedonian',
243 'mh' => 'Marshallese',
244 'ml' => 'Malayalam',
245 'mi' => 'Maori',
246 'mr' => 'Marathi',
247 'ms' => 'Malay',
248 'mg' => 'Malagasy',
249 'mt' => 'Maltese',
250 'mo' => 'Moldavian',
251 'mn' => 'Mongolian',
252 'na' => 'Nauru',
253 'nv' => 'Navajo; Navaho',
254 'nr' => 'Ndebele, South; South Ndebele',
255 'nd' => 'Ndebele, North; North Ndebele',
256 'ng' => 'Ndonga',
257 'ne' => 'Nepali',
258 'nn' => 'Norwegian Nynorsk; Nynorsk, Norwegian',
259 'nb' => 'Bokmål, Norwegian, Norwegian Bokmål',
260 'no' => 'Norwegian',
261 'ny' => 'Chichewa; Chewa; Nyanja',
262 'oc' => 'Occitan, Provençal',
263 'oj' => 'Ojibwa',
264 'or' => 'Oriya',
265 'om' => 'Oromo',
266 'os' => 'Ossetian; Ossetic',
267 'pa' => 'Panjabi; Punjabi',
268 'fa' => 'Persian',
269 'pi' => 'Pali',
270 'pl' => 'Polish',
271 'pt' => 'Portuguese',
272 'ps' => 'Pushto',
273 'qu' => 'Quechua',
274 'rm' => 'Romansh',
275 'ro' => 'Romanian',
276 'rn' => 'Rundi',
277 'ru' => 'Russian',
278 'sg' => 'Sango',
279 'sa' => 'Sanskrit',
280 'sr' => 'Serbian',
281 'hr' => 'Croatian',
282 'si' => 'Sinhala; Sinhalese',
283 'sk' => 'Slovak',
284 'sl' => 'Slovenian',
285 'se' => 'Northern Sami',
286 'sm' => 'Samoan',
287 'sn' => 'Shona',
288 'sd' => 'Sindhi',
289 'so' => 'Somali',
290 'st' => 'Sotho, Southern',
291 'es' => 'Spanish; Castilian',
292 'sc' => 'Sardinian',
293 'ss' => 'Swati',
294 'su' => 'Sundanese',
295 'sw' => 'Swahili',
296 'sv' => 'Swedish',
297 'ty' => 'Tahitian',
298 'ta' => 'Tamil',
299 'tt' => 'Tatar',
300 'te' => 'Telugu',
301 'tg' => 'Tajik',
302 'tl' => 'Tagalog',
303 'th' => 'Thai',
304 'bo' => 'Tibetan',
305 'ti' => 'Tigrinya',
306 'to' => 'Tonga (Tonga Islands)',
307 'tn' => 'Tswana',
308 'ts' => 'Tsonga',
309 'tk' => 'Turkmen',
310 'tr' => 'Turkish',
311 'tw' => 'Twi',
312 'ug' => 'Uighur; Uyghur',
313 'uk' => 'Ukrainian',
314 'ur' => 'Urdu',
315 'uz' => 'Uzbek',
316 've' => 'Venda',
317 'vi' => 'Vietnamese',
318 'vo' => 'Volapük',
319 'cy' => 'Welsh',
320 'wa' => 'Walloon',
321 'wo' => 'Wolof',
322 'xh' => 'Xhosa',
323 'yi' => 'Yiddish',
324 'yo' => 'Yoruba',
325 'za' => 'Zhuang; Chuang',
326 'zu' => 'Zulu',
327 );
328 $humanLanguage = strtr( $code, $lang_codes );
329 $lang = apply_filters( 'wpml_post_language_details', null, $postId );
330 if ( !empty( $lang ) ) {
331 $locale = $lang['locale'];
332 $humanLanguage = $lang['display_name'];
333 }
334 return strtolower( "$locale ($humanLanguage)" );
335 }
336 #endregion
337
338 #region Users/Sessions Helpers
339
340 function get_session_id() {
341 if ( isset( $_COOKIE['mwai_session_id'] ) ) {
342 return $_COOKIE['mwai_session_id'];
343 }
344 return "N/A";
345 }
346
347 // Get the UserID from the data, or from the current user
348 function get_user_id( $data = null ) {
349 if ( isset( $data ) && isset( $data['userId'] ) ) {
350 return (int)$data['userId'];
351 }
352 if ( is_user_logged_in() ) {
353 $current_user = wp_get_current_user();
354 if ( $current_user->ID > 0 ) {
355 return $current_user->ID;
356 }
357 }
358 return null;
359 }
360
361 function getUserData() {
362 $user = wp_get_current_user();
363 $placeholders = array(
364 'FIRST_NAME' => get_user_meta($user->ID, 'first_name', true),
365 'LAST_NAME' => get_user_meta($user->ID, 'last_name', true),
366 'USER_LOGIN' => $user->data->user_login,
367 'DISPLAY_NAME' => $user->data->display_name,
368 'AVATAR_URL' => get_avatar_url( get_current_user_id() ),
369 );
370 return $placeholders;
371 }
372
373 function get_ip_address( $data = null ) {
374 if ( isset( $data ) && isset( $data['ip'] ) ) {
375 $data['ip'] = (string)$data['ip'];
376 }
377 else {
378 if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
379 $data['ip'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
380 }
381 else if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
382 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_CLIENT_IP'] );
383 }
384 else if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
385 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] );
386 }
387 }
388 return $data['ip'];
389 }
390
391 #endregion
392
393 #region Other Helpers
394
395 function isUrl( $url ) {
396 return strpos( $url, 'http' ) === 0 ? true : false;
397 }
398
399 function getPostTypes() {
400 $excluded = array( 'attachment', 'revision', 'nav_menu_item' );
401 $post_types = array();
402 $types = get_post_types( array( 'public' => true ), 'objects' );
403 foreach ( $types as $type ) {
404 if ( in_array( $type->name, $excluded ) ) {
405 continue;
406 }
407 $post_types[] = array(
408 'name' => $type->labels->name,
409 'type' => $type->name,
410 );
411 }
412 return $post_types;
413 }
414
415 function getCleanPost( $post ) {
416 if ( is_object( $post ) ) {
417 $post = (array)$post;
418 }
419 $language = $this->get_post_language( $post['ID'] );
420 $content = apply_filters( 'mwai_pre_post_content', $post['post_content'], $post['ID'] );
421 $content = $this->cleanText( $content );
422 $content = apply_filters( 'mwai_post_content', $content, $post['ID'] );
423 $title = $post['post_title'];
424 $excerpt = $post['post_excerpt'];
425 $url = get_permalink( $post['ID'] );
426 $checksum = wp_hash( $content . $title . $url );
427 return [
428 'postId' => $post['ID'],
429 'title' => $title,
430 'content' => $content,
431 'excerpt' => $excerpt,
432 'url' => $url,
433 'language' => $language,
434 'checksum' => $checksum,
435 ];
436 }
437
438 #endregion
439
440 #region Usage & Costs
441
442 public function dynamic_max_tokens( $tokens, $text ) {
443 // Approximation (fast, no lib)
444 $asciiCount = 0;
445 $nonAsciiCount = 0;
446 for ( $i = 0; $i < mb_strlen( $text ); $i++ ) {
447 $char = mb_substr( $text, $i, 1 );
448 if ( ord( $char ) < 128 ) {
449 $asciiCount++;
450 }
451 else {
452 $nonAsciiCount++;
453 }
454 }
455 $asciiTokens = $asciiCount / 3.5;
456 $nonAsciiTokens = $nonAsciiCount * 2.5;
457 $tokens = $asciiTokens + $nonAsciiTokens;
458
459 // More exact (slower, and lib)
460 if ( PHP_VERSION_ID >= 70400 && function_exists( 'mb_convert_encoding' ) ) {
461 try {
462 $token_array = Encoder::encode( $text );
463 if ( !empty( $token_array ) ) {
464 $tokens = count( $token_array );
465 }
466 }
467 catch ( Exception $e ) {
468 error_log( $e->getMessage() );
469 }
470 }
471
472 $tokens = $tokens;
473 return (int)$tokens;
474 }
475
476 public function record_tokens_usage( $model, $prompt_tokens, $completion_tokens = 0 ) {
477 if ( !is_numeric( $prompt_tokens ) ) {
478 throw new Exception( 'Record usage: prompt_tokens is not a number.' );
479 }
480 if ( !is_numeric( $completion_tokens ) ) {
481 $completion_tokens = 0;
482 }
483 if ( !$model ) {
484 throw new Exception( 'Record usage: model is missing.' );
485 }
486 $usage = $this->get_option( 'openai_usage' );
487 $month = date( 'Y-m' );
488 if ( !isset( $usage[$month] ) ) {
489 $usage[$month] = array();
490 }
491 if ( !isset( $usage[$month][$model] ) ) {
492 $usage[$month][$model] = array(
493 'prompt_tokens' => 0,
494 'completion_tokens' => 0,
495 'total_tokens' => 0
496 );
497 }
498 $usage[$month][$model]['prompt_tokens'] += $prompt_tokens;
499 $usage[$month][$model]['completion_tokens'] += $completion_tokens;
500 $usage[$month][$model]['total_tokens'] += $prompt_tokens + $completion_tokens;
501 $this->update_option( 'openai_usage', $usage );
502 return [
503 'prompt_tokens' => $prompt_tokens,
504 'completion_tokens' => $completion_tokens,
505 'total_tokens' => $prompt_tokens + $completion_tokens
506 ];
507 }
508
509 public function record_images_usage( $model, $resolution, $images ) {
510 if ( !$model || !$resolution || !$images ) {
511 throw new Exception( 'Missing parameters for record_image_usage.' );
512 }
513 $usage = $this->get_option( 'openai_usage' );
514 $month = date( 'Y-m' );
515 if ( !isset( $usage[$month] ) ) {
516 $usage[$month] = array();
517 }
518 if ( !isset( $usage[$month][$model] ) ) {
519 $usage[$month][$model] = array(
520 'resolution' => array(),
521 'images' => 0
522 );
523 }
524 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
525 $usage[$month][$model]['resolution'][$resolution] = 0;
526 }
527 $usage[$month][$model]['resolution'][$resolution] += $images;
528 $usage[$month][$model]['images'] += $images;
529 $this->update_option( 'openai_usage', $usage );
530 return [
531 'resolution' => $resolution,
532 'images' => $images
533 ];
534 }
535
536 #endregion
537
538 #region Options
539 function get_all_options() {
540 $options = get_option( $this->option_name, null );
541 foreach ( MWAI_OPTIONS as $key => $value ) {
542 if ( !isset( $options[$key] ) ) {
543 $options[$key] = $value;
544 }
545 if ( $key === 'languages' ) {
546 // TODO: If we decide to make a set of options for languages, we can keep it in the settings
547 $options[$key] = MWAI_LANGUAGES;
548 $options[$key] = apply_filters( 'mwai_languages', $options[$key] );
549 }
550 }
551 $options['shortcode_chat_default_params'] = MWAI_CHATBOT_PARAMS;
552 $options['default_limits'] = MWAI_LIMITS;
553 $options['openai_models'] = MWAI_OPENAI_MODELS;
554 return $options;
555 }
556
557 // Validate and keep the options clean and logical.
558 function sanitize_options() {
559 $options = $this->get_all_options();
560 $needs_update = false;
561
562 // We can sanitize our future options here, let's always remember it.
563 // Now, it is empty...
564
565 if ( $needs_update ) {
566 update_option( $this->option_name, $options, false );
567 }
568 return $options;
569 }
570
571 function update_options( $options ) {
572 if ( !update_option( $this->option_name, $options, false ) ) {
573 return false;
574 }
575 $options = $this->sanitize_options();
576 return $options;
577 }
578
579 function update_option( $option, $value ) {
580 $options = $this->get_all_options();
581 $options[$option] = $value;
582 return $this->update_options( $options );
583 }
584
585 function get_option( $option, $default = null ) {
586 $options = $this->get_all_options();
587 return $options[$option] ?? $default;
588 }
589 #endregion
590 }
591
592 ?>