PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.91
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.91
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 ui.php 3 years ago
core.php
578 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 if ( $this->is_rest ) {
35 new Meow_MWAI_Rest( $this );
36 }
37 if ( is_admin() ) {
38 new Meow_MWAI_Admin( $this );
39 new Meow_MWAI_Modules_Assistants( $this );
40 }
41 else {
42 //new Meow_MWAI_UI( $this );
43 if ( $this->get_option( 'shortcode_chat' ) ) {
44 new Meow_MWAI_Modules_Chatbot();
45 }
46 }
47
48 // Advanced core
49 if ( class_exists( 'MeowPro_MWAI_Core' ) ) {
50 new MeowPro_MWAI_Core( $this );
51 }
52
53 // Dynamic max tokens
54 if ( $this->get_option( 'dynamic_max_tokens' ) ) {
55 add_filter( 'mwai_estimate_tokens', array( $this, 'dynamic_max_tokens' ), 10, 2 );
56 }
57 }
58
59 #region Roles & Capabilities
60
61 function can_access_settings() {
62 return apply_filters( 'mwai_allow_setup', current_user_can( 'manage_options' ) );
63 }
64
65 function can_access_features() {
66 $editor_or_admin = current_user_can( 'editor' ) || current_user_can( 'administrator' );
67 return apply_filters( 'mwai_allow_usage', $editor_or_admin );
68 }
69
70 #endregion
71
72 #region Text-Related Helpers
73
74 // Clean the text perfectly, resolve shortcodes, etc, etc.
75 function cleanText( $rawText = "" ) {
76 $text = html_entity_decode( $rawText );
77 $text = wp_strip_all_tags( $text );
78 $text = preg_replace( '/[\r\n]+/', "\n", $text );
79 return $text . " ";
80
81 // Before simplification:
82 // $text = strip_tags( $rawText );
83 // $text = strip_shortcodes( $text );
84 // $text = html_entity_decode( $text );
85 // $text = preg_replace( '/[\r\n]+/', "\n", $text );
86 // $sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
87 // foreach ( $sentences as $key => $sentence ) {
88 // $sentences[$key] = trim( $sentence );
89 // }
90 // $text = implode( " ", $sentences );
91 // $text = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $text );
92 // return $text . " ";
93 }
94
95 // Make sure there are no duplicate sentences, and keep the length under a maximum length.
96 function cleanSentences( $text, $maxTokens = 512 ) {
97 //$sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
98 $sentences = preg_split('/(?<=[.?!。.!?])+/u', $text);
99 $hashes = array();
100 $uniqueSentences = array();
101 $length = 0;
102 foreach ( $sentences as $sentence ) {
103 $sentence = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $sentence );
104 $hash = md5( $sentence );
105 if ( !in_array( $hash, $hashes ) ) {
106 $tokensCount = apply_filters( 'mwai_estimate_tokens', 0, $sentence );
107 if ( $length + $tokensCount > $maxTokens ) {
108 continue;
109 }
110 $hashes[] = $hash;
111 $uniqueSentences[] = $sentence;
112 $length += $tokensCount;
113 }
114 }
115 $freshText = implode( " ", $uniqueSentences );
116 $freshText = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $freshText );
117 return $freshText;
118 }
119
120 function getCleanPostContent( $postId ) {
121 $post = get_post( $postId );
122 if ( !$post ) {
123 return false;
124 }
125 $post->post_content = apply_filters( 'the_content', $post->post_content );
126 $text = $this->cleanText( $post->post_content );
127 $text = $this->cleanSentences( $text );
128 return $text;
129 }
130
131 function markdown_to_html( $content ) {
132 $Parsedown = new Parsedown();
133 $content = $Parsedown->text( $content );
134 return $content;
135 }
136
137 function get_post_language( $postId ) {
138 $locale = get_locale();
139 $code = strtolower( substr( $locale, 0, 2 ) );
140 $lang_codes = array(
141 'aa' => 'Afar',
142 'ab' => 'Abkhazian',
143 'af' => 'Afrikaans',
144 'ak' => 'Akan',
145 'sq' => 'Albanian',
146 'am' => 'Amharic',
147 'ar' => 'Arabic',
148 'an' => 'Aragonese',
149 'hy' => 'Armenian',
150 'as' => 'Assamese',
151 'av' => 'Avaric',
152 'ae' => 'Avestan',
153 'ay' => 'Aymara',
154 'az' => 'Azerbaijani',
155 'ba' => 'Bashkir',
156 'bm' => 'Bambara',
157 'eu' => 'Basque',
158 'be' => 'Belarusian',
159 'bn' => 'Bengali',
160 'bh' => 'Bihari',
161 'bi' => 'Bislama',
162 'bs' => 'Bosnian',
163 'br' => 'Breton',
164 'bg' => 'Bulgarian',
165 'my' => 'Burmese',
166 'ca' => 'Catalan; Valencian',
167 'ch' => 'Chamorro',
168 'ce' => 'Chechen',
169 'zh' => 'Chinese',
170 'cu' => 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic',
171 'cv' => 'Chuvash',
172 'kw' => 'Cornish',
173 'co' => 'Corsican',
174 'cr' => 'Cree',
175 'cs' => 'Czech',
176 'da' => 'Danish',
177 'dv' => 'Divehi; Dhivehi; Maldivian',
178 'nl' => 'Dutch; Flemish',
179 'dz' => 'Dzongkha',
180 'en' => 'English',
181 'eo' => 'Esperanto',
182 'et' => 'Estonian',
183 'ee' => 'Ewe',
184 'fo' => 'Faroese',
185 'fj' => 'Fijjian',
186 'fi' => 'Finnish',
187 'fr' => 'French',
188 'fy' => 'Western Frisian',
189 'ff' => 'Fulah',
190 'ka' => 'Georgian',
191 'de' => 'German',
192 'gd' => 'Gaelic; Scottish Gaelic',
193 'ga' => 'Irish',
194 'gl' => 'Galician',
195 'gv' => 'Manx',
196 'el' => 'Greek, Modern',
197 'gn' => 'Guarani',
198 'gu' => 'Gujarati',
199 'ht' => 'Haitian; Haitian Creole',
200 'ha' => 'Hausa',
201 'he' => 'Hebrew',
202 'hz' => 'Herero',
203 'hi' => 'Hindi',
204 'ho' => 'Hiri Motu',
205 'hu' => 'Hungarian',
206 'ig' => 'Igbo',
207 'is' => 'Icelandic',
208 'io' => 'Ido',
209 'ii' => 'Sichuan Yi',
210 'iu' => 'Inuktitut',
211 'ie' => 'Interlingue',
212 'ia' => 'Interlingua (International Auxiliary Language Association)',
213 'id' => 'Indonesian',
214 'ik' => 'Inupiaq',
215 'it' => 'Italian',
216 'jv' => 'Javanese',
217 'ja' => 'Japanese',
218 'kl' => 'Kalaallisut; Greenlandic',
219 'kn' => 'Kannada',
220 'ks' => 'Kashmiri',
221 'kr' => 'Kanuri',
222 'kk' => 'Kazakh',
223 'km' => 'Central Khmer',
224 'ki' => 'Kikuyu; Gikuyu',
225 'rw' => 'Kinyarwanda',
226 'ky' => 'Kirghiz; Kyrgyz',
227 'kv' => 'Komi',
228 'kg' => 'Kongo',
229 'ko' => 'Korean',
230 'kj' => 'Kuanyama; Kwanyama',
231 'ku' => 'Kurdish',
232 'lo' => 'Lao',
233 'la' => 'Latin',
234 'lv' => 'Latvian',
235 'li' => 'Limburgan; Limburger; Limburgish',
236 'ln' => 'Lingala',
237 'lt' => 'Lithuanian',
238 'lb' => 'Luxembourgish; Letzeburgesch',
239 'lu' => 'Luba-Katanga',
240 'lg' => 'Ganda',
241 'mk' => 'Macedonian',
242 'mh' => 'Marshallese',
243 'ml' => 'Malayalam',
244 'mi' => 'Maori',
245 'mr' => 'Marathi',
246 'ms' => 'Malay',
247 'mg' => 'Malagasy',
248 'mt' => 'Maltese',
249 'mo' => 'Moldavian',
250 'mn' => 'Mongolian',
251 'na' => 'Nauru',
252 'nv' => 'Navajo; Navaho',
253 'nr' => 'Ndebele, South; South Ndebele',
254 'nd' => 'Ndebele, North; North Ndebele',
255 'ng' => 'Ndonga',
256 'ne' => 'Nepali',
257 'nn' => 'Norwegian Nynorsk; Nynorsk, Norwegian',
258 'nb' => 'Bokmål, Norwegian, Norwegian Bokmål',
259 'no' => 'Norwegian',
260 'ny' => 'Chichewa; Chewa; Nyanja',
261 'oc' => 'Occitan, Provençal',
262 'oj' => 'Ojibwa',
263 'or' => 'Oriya',
264 'om' => 'Oromo',
265 'os' => 'Ossetian; Ossetic',
266 'pa' => 'Panjabi; Punjabi',
267 'fa' => 'Persian',
268 'pi' => 'Pali',
269 'pl' => 'Polish',
270 'pt' => 'Portuguese',
271 'ps' => 'Pushto',
272 'qu' => 'Quechua',
273 'rm' => 'Romansh',
274 'ro' => 'Romanian',
275 'rn' => 'Rundi',
276 'ru' => 'Russian',
277 'sg' => 'Sango',
278 'sa' => 'Sanskrit',
279 'sr' => 'Serbian',
280 'hr' => 'Croatian',
281 'si' => 'Sinhala; Sinhalese',
282 'sk' => 'Slovak',
283 'sl' => 'Slovenian',
284 'se' => 'Northern Sami',
285 'sm' => 'Samoan',
286 'sn' => 'Shona',
287 'sd' => 'Sindhi',
288 'so' => 'Somali',
289 'st' => 'Sotho, Southern',
290 'es' => 'Spanish; Castilian',
291 'sc' => 'Sardinian',
292 'ss' => 'Swati',
293 'su' => 'Sundanese',
294 'sw' => 'Swahili',
295 'sv' => 'Swedish',
296 'ty' => 'Tahitian',
297 'ta' => 'Tamil',
298 'tt' => 'Tatar',
299 'te' => 'Telugu',
300 'tg' => 'Tajik',
301 'tl' => 'Tagalog',
302 'th' => 'Thai',
303 'bo' => 'Tibetan',
304 'ti' => 'Tigrinya',
305 'to' => 'Tonga (Tonga Islands)',
306 'tn' => 'Tswana',
307 'ts' => 'Tsonga',
308 'tk' => 'Turkmen',
309 'tr' => 'Turkish',
310 'tw' => 'Twi',
311 'ug' => 'Uighur; Uyghur',
312 'uk' => 'Ukrainian',
313 'ur' => 'Urdu',
314 'uz' => 'Uzbek',
315 've' => 'Venda',
316 'vi' => 'Vietnamese',
317 'vo' => 'Volapük',
318 'cy' => 'Welsh',
319 'wa' => 'Walloon',
320 'wo' => 'Wolof',
321 'xh' => 'Xhosa',
322 'yi' => 'Yiddish',
323 'yo' => 'Yoruba',
324 'za' => 'Zhuang; Chuang',
325 'zu' => 'Zulu',
326 );
327 $language = strtr( $code, $lang_codes );
328 $lang = apply_filters( 'wpml_post_language_details', null, $postId );
329 if ( !empty( $lang ) ) {
330 $language = $lang['display_name'];
331 }
332 return strtolower( $language );
333 }
334 #endregion
335
336 #region Users/Sessions Helpers
337
338 function get_session_id() {
339 if ( isset( $_COOKIE['mwai_session_id'] ) ) {
340 return $_COOKIE['mwai_session_id'];
341 }
342 return "N/A";
343 }
344
345 // Get the UserID from the data, or from the current user
346 function get_user_id( $data = null ) {
347 if ( isset( $data ) && isset( $data['userId'] ) ) {
348 return (int)$data['userId'];
349 }
350 if ( is_user_logged_in() ) {
351 $current_user = wp_get_current_user();
352 if ( $current_user->ID > 0 ) {
353 return $current_user->ID;
354 }
355 }
356 return null;
357 }
358
359 function get_ip_address( $data = null ) {
360 if ( isset( $data ) && isset( $data['ip'] ) ) {
361 $data['ip'] = (string)$data['ip'];
362 }
363 else {
364 if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
365 $data['ip'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
366 }
367 else if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
368 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_CLIENT_IP'] );
369 }
370 else if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
371 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] );
372 }
373 }
374 return $data['ip'];
375 }
376
377 #endregion
378
379 #region Other Helpers
380
381 function isUrl( $url ) {
382 return strpos( $url, 'http' ) === 0 ? true : false;
383 }
384
385 function getPostTypes() {
386 $excluded = array( 'attachment', 'revision', 'nav_menu_item' );
387 $post_types = array();
388 $types = get_post_types( array( 'public' => true ), 'objects' );
389 foreach ( $types as $type ) {
390 if ( in_array( $type->name, $excluded ) ) {
391 continue;
392 }
393 $post_types[] = array(
394 'name' => $type->labels->name,
395 'type' => $type->name,
396 );
397 }
398 return $post_types;
399 }
400
401 function getCleanPost( $post ) {
402 if ( is_object( $post ) ) {
403 $post = (array)$post;
404 }
405 $language = $this->get_post_language( $post['ID'] );
406 $content = apply_filters( 'mwai_pre_post_content', $post['post_content'], $post['ID'] );
407 $content = $this->cleanText( $content );
408 $content = apply_filters( 'mwai_post_content', $content, $post['ID'] );
409 $title = $post['post_title'];
410 $excerpt = $post['post_excerpt'];
411 $url = get_permalink( $post['ID'] );
412 $checksum = wp_hash( $content . $title . $url );
413 return [
414 'postId' => $post['ID'],
415 'title' => $title,
416 'content' => $content,
417 'excerpt' => $excerpt,
418 'url' => $url,
419 'language' => $language,
420 'checksum' => $checksum,
421 ];
422 }
423
424 #endregion
425
426 #region Usage & Costs
427
428 public function dynamic_max_tokens( $tokens, $text ) {
429 // Approximation (fast, no lib)
430 $asciiCount = 0;
431 $nonAsciiCount = 0;
432 for ( $i = 0; $i < mb_strlen( $text ); $i++ ) {
433 $char = mb_substr( $text, $i, 1 );
434 if ( ord( $char ) < 128 ) {
435 $asciiCount++;
436 }
437 else {
438 $nonAsciiCount++;
439 }
440 }
441 $asciiTokens = $asciiCount / 3.5;
442 $nonAsciiTokens = $nonAsciiCount * 2.5;
443 $tokens = $asciiTokens + $nonAsciiTokens;
444
445 // More exact (slower, and lib)
446 if ( PHP_VERSION_ID >= 70400 && function_exists( 'mb_convert_encoding' ) ) {
447 try {
448 $token_array = Encoder::encode( $text );
449 if ( !empty( $token_array ) ) {
450 $tokens = count( $token_array );
451 }
452 }
453 catch ( Exception $e ) {
454 error_log( $e->getMessage() );
455 }
456 }
457
458 $tokens = $tokens;
459 return (int)$tokens;
460 }
461
462 public function record_tokens_usage( $model, $prompt_tokens, $completion_tokens = 0 ) {
463 if ( !is_numeric( $prompt_tokens ) ) {
464 throw new Exception( 'Record usage: prompt_tokens is not a number.' );
465 }
466 if ( !is_numeric( $completion_tokens ) ) {
467 $completion_tokens = 0;
468 }
469 if ( !$model ) {
470 throw new Exception( 'Record usage: model is missing.' );
471 }
472 $usage = $this->get_option( 'openai_usage' );
473 $month = date( 'Y-m' );
474 if ( !isset( $usage[$month] ) ) {
475 $usage[$month] = array();
476 }
477 if ( !isset( $usage[$month][$model] ) ) {
478 $usage[$month][$model] = array(
479 'prompt_tokens' => 0,
480 'completion_tokens' => 0,
481 'total_tokens' => 0
482 );
483 }
484 $usage[$month][$model]['prompt_tokens'] += $prompt_tokens;
485 $usage[$month][$model]['completion_tokens'] += $completion_tokens;
486 $usage[$month][$model]['total_tokens'] += $prompt_tokens + $completion_tokens;
487 $this->update_option( 'openai_usage', $usage );
488 return [
489 'prompt_tokens' => $prompt_tokens,
490 'completion_tokens' => $completion_tokens,
491 'total_tokens' => $prompt_tokens + $completion_tokens
492 ];
493 }
494
495 public function record_images_usage( $model, $resolution, $images ) {
496 if ( !$model || !$resolution || !$images ) {
497 throw new Exception( 'Missing parameters for record_image_usage.' );
498 }
499 $usage = $this->get_option( 'openai_usage' );
500 $month = date( 'Y-m' );
501 if ( !isset( $usage[$month] ) ) {
502 $usage[$month] = array();
503 }
504 if ( !isset( $usage[$month][$model] ) ) {
505 $usage[$month][$model] = array(
506 'resolution' => array(),
507 'images' => 0
508 );
509 }
510 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
511 $usage[$month][$model]['resolution'][$resolution] = 0;
512 }
513 $usage[$month][$model]['resolution'][$resolution] += $images;
514 $usage[$month][$model]['images'] += $images;
515 $this->update_option( 'openai_usage', $usage );
516 return [
517 'resolution' => $resolution,
518 'images' => $images
519 ];
520 }
521
522 #endregion
523
524 #region Options
525 function get_all_options() {
526 $options = get_option( $this->option_name, null );
527 foreach ( MWAI_OPTIONS as $key => $value ) {
528 if ( !isset( $options[$key] ) ) {
529 $options[$key] = $value;
530 }
531 if ( $key === 'languages' ) {
532 // TODO: If we decide to make a set of options for languages, we can keep it in the settings
533 $options[$key] = MWAI_LANGUAGES;
534 $options[$key] = apply_filters( 'mwai_languages', $options[$key] );
535 }
536 }
537 $options['shortcode_chat_default_params'] = MWAI_CHATBOT_PARAMS;
538 $options['default_limits'] = MWAI_LIMITS;
539 $options['openai_models'] = MWAI_OPENAI_MODELS;
540 return $options;
541 }
542
543 // Validate and keep the options clean and logical.
544 function sanitize_options() {
545 $options = $this->get_all_options();
546 $needs_update = false;
547
548 // We can sanitize our future options here, let's always remember it.
549 // Now, it is empty...
550
551 if ( $needs_update ) {
552 update_option( $this->option_name, $options, false );
553 }
554 return $options;
555 }
556
557 function update_options( $options ) {
558 if ( !update_option( $this->option_name, $options, false ) ) {
559 return false;
560 }
561 $options = $this->sanitize_options();
562 return $options;
563 }
564
565 function update_option( $option, $value ) {
566 $options = $this->get_all_options();
567 $options[$option] = $value;
568 return $this->update_options( $options );
569 }
570
571 function get_option( $option, $default = null ) {
572 $options = $this->get_all_options();
573 return $options[$option] ?? $default;
574 }
575 #endregion
576 }
577
578 ?>