Changeset 3354820
- Timestamp:
- 09/02/2025 03:09:54 PM (7 months ago)
- Location:
- s2b-ai-assistant/trunk
- Files:
-
- 12 edited
-
lib/S2bAia.php (modified) (2 diffs)
-
lib/controllers/AdminChatBotController.php (modified) (1 diff)
-
lib/controllers/AdminController.php (modified) (3 diffs)
-
lib/controllers/ChatBotController.php (modified) (9 diffs)
-
lib/helpers/AiRequest.php (modified) (6 diffs)
-
lib/helpers/UpdateUtils.php (modified) (5 diffs)
-
lib/helpers/WpHttpClient.php (modified) (4 diffs)
-
lib/integrations/anthropic/Anthropic.php (modified) (2 diffs)
-
lib/integrations/deepseek/DeepSeek.php (modified) (2 diffs)
-
readme.txt (modified) (2 diffs)
-
s2b-ai-assistant.php (modified) (2 diffs)
-
views/backend/chatbot/chatbot_general.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
s2b-ai-assistant/trunk/lib/S2bAia.php
r3338464 r3354820 43 43 44 44 $charset_collate = $wpdb->get_charset_collate(); 45 //$table_name = $wpdb->prefix . 's2baia_instructions'; 45 46 47 //require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 46 48 $wpdb->query(/* phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange *//* phpcs:ignore WordPress.DB.DirectDatabaseQuery */ 47 49 sprintf( … … 56 58 $charset_collate 57 59 )); 58 59 60 61 60 62 $wpdb->insert(/* phpcs:ignore WordPress.DB.DirectDatabaseQuery */ 61 63 $wpdb->prefix . 's2baia_instructions', array( -
s2b-ai-assistant/trunk/lib/controllers/AdminChatBotController.php
r3338464 r3354820 453 453 } 454 454 455 if (isset($_POST['s2baia_chatbot_use_usage'])) { 456 if($_POST['s2baia_chatbot_use_usage'] == 'on'){ 457 $data['use_usage'] = 1; 458 update_option('s2baia_use_usage', 1); 459 }else{ 460 $data['use_usage'] = 0; 461 update_option('s2baia_use_usage', 0); 462 } 463 }else{ 464 $data['use_usage'] = 0; 465 update_option('s2baia_use_usage', 0); 466 } 467 455 468 if (isset($_POST['s2baia_chatbot_open_stream2'])) { 456 469 if($_POST['s2baia_chatbot_open_stream2'] == 'on'){ -
s2b-ai-assistant/trunk/lib/controllers/AdminController.php
r3318367 r3354820 14 14 public $chatbot_controller = false; 15 15 public $rag_controller = false; 16 public $usage_controller = false; 16 17 public $admlogo = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjMiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAyMyAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTcuMjg2OTIgMy44MTgxOEM3LjIyMyAzLjI3ODQxIDYuOTYzNzcgMi44NTkzNyA2LjUwOTIyIDIuNTYxMDhDNi4wNTQ2OCAyLjI2Mjc4IDUuNDk3MTUgMi4xMTM2MyA0LjgzNjY0IDIuMTEzNjNDNC4zNTM2OSAyLjExMzYzIDMuOTMxMSAyLjE5MTc2IDMuNTY4ODggMi4zNDgwMUMzLjIxMDIyIDIuNTA0MjYgMi45Mjk2OCAyLjcxOTEgMi43MjcyNiAyLjk5MjU0QzIuNTI4NCAzLjI2NTk4IDIuNDI4OTcgMy41NzY3IDIuNDI4OTcgMy45MjQ3MUMyLjQyODk3IDQuMjE1OSAyLjQ5ODIyIDQuNDY2MjYgMi42MzY3MSA0LjY3NTc4QzIuNzc4NzYgNC44ODE3NCAyLjk1OTg2IDUuMDUzOTcgMy4xODAwMyA1LjE5MjQ3QzMuNDAwMiA1LjMyNzQxIDMuNjMxMDMgNS40MzkyNyAzLjg3MjUxIDUuNTI4MDVDNC4xMTM5OCA1LjYxMzI4IDQuMzM1OTMgNS42ODI1MiA0LjUzODM0IDUuNzM1NzlMNS42NDYzIDYuMDM0MDlDNS45MzAzOSA2LjEwODY2IDYuMjQ2NDQgNi4yMTE2NCA2LjU5NDQ1IDYuMzQzMDRDNi45NDYwMSA2LjQ3NDQzIDcuMjgxNiA2LjY1Mzc2IDcuNjAxMiA2Ljg4MTAzQzcuOTI0MzUgNy4xMDQ3NSA4LjE5MDY5IDcuMzkyNCA4LjQwMDIxIDcuNzQzOTZDOC42MDk3MiA4LjA5NTUyIDguNzE0NDggOC41MjY5OCA4LjcxNDQ4IDkuMDM4MzVDOC43MTQ0OCA5LjYyNzg0IDguNTYwMDEgMTAuMTYwNSA4LjI1MTA2IDEwLjYzNjRDNy45NDU2NiAxMS4xMTIyIDcuNDk4MjIgMTEuNDkwNCA2LjkwODczIDExLjc3MDlDNi4zMjI3OSAxMi4wNTE1IDUuNjEwNzkgMTIuMTkxOCA0Ljc3MjcyIDEyLjE5MThDMy45OTE0NyAxMi4xOTE4IDMuMzE0OTggMTIuMDY1NyAyLjc0MzI0IDExLjgxMzZDMi4xNzUwNiAxMS41NjE0IDEuNzI3NjIgMTEuMjA5OSAxLjQwMDkyIDEwLjc1ODlDMS4wNzc3NiAxMC4zMDc5IDAuODk0ODc4IDkuNzg0MDkgMC44NTIyNjQgOS4xODc1SDIuMjE1OUMyLjI1MTQxIDkuNTk5NDMgMi4zODk5MSA5Ljk0MDM0IDIuNjMxMzggMTAuMjEwMkMyLjg3NjQxIDEwLjQ3NjYgMy4xODUzNiAxMC42NzU0IDMuNTU4MjMgMTAuODA2OEMzLjkzNDY1IDEwLjkzNDcgNC4zMzk0OCAxMC45OTg2IDQuNzcyNzIgMTAuOTk4NkM1LjI3Njk4IDEwLjk5ODYgNS43Mjk3NSAxMC45MTY5IDYuMTMxMDMgMTAuNzUzNUM2LjUzMjMxIDEwLjU4NjYgNi44NTAxMyAxMC4zNTU4IDcuMDg0NTEgMTAuMDYxMUM3LjMxODg4IDkuNzYyNzggNy40MzYwNyA5LjQxNDc3IDcuNDM2MDcgOS4wMTcwNEM3LjQzNjA3IDguNjU0ODMgNy4zMzQ4NiA4LjM2MDA4IDcuMTMyNDUgOC4xMzI4MUM2LjkzMDAzIDcuOTA1NTQgNi42NjM3IDcuNzIwODggNi4zMzM0NCA3LjU3ODgzQzYuMDAzMTkgNy40MzY3OSA1LjY0NjMgNy4zMTI1IDUuMjYyNzggNy4yMDU5NkwzLjkyMDQ1IDYuODIyNDRDMy4wNjgxNyA2LjU3NzQxIDIuMzkzNDYgNi4yMjc2MiAxLjg5NjMgNS43NzMwOEMxLjM5OTE0IDUuMzE4NTMgMS4xNTA1NiA0LjcyMzcyIDEuMTUwNTYgMy45ODg2M0MxLjE1MDU2IDMuMzc3ODQgMS4zMTU2OSAyLjg0NTE3IDEuNjQ1OTQgMi4zOTA2MkMxLjk3OTc1IDEuOTMyNTIgMi40MjcxOSAxLjU3NzQxIDIuOTg4MjcgMS4zMjUyOEMzLjU1MjkgMS4wNjk2IDQuMTgzMjMgMC45NDE3NTcgNC44NzkyNSAwLjk0MTc1N0M1LjU4MjM4IDAuOTQxNzU3IDYuMjA3MzggMS4wNjc4MiA2Ljc1NDI1IDEuMzE5OTVDNy4zMDExMyAxLjU2ODUzIDcuNzM0MzcgMS45MDk0NCA4LjA1Mzk3IDIuMzQyNjhDOC4zNzcxMiAyLjc3NTkyIDguNTQ3NTggMy4yNjc3NSA4LjU2NTMzIDMuODE4MThINy4yODY5MloiIGZpbGw9IiNGRjA2MDYiLz4KPHBhdGggZD0iTTE1LjMyMSAxMlYxLjA5MDkxSDE5LjEzNDlDMTkuODk0OSAxLjA5MDkxIDIwLjUyMTcgMS4yMjIzIDIxLjAxNTMgMS40ODUwOEMyMS41MDg5IDEuNzQ0MzEgMjEuODc2NCAyLjA5NDEgMjIuMTE3OSAyLjUzNDQ0QzIyLjM1OTQgMi45NzEyMyAyMi40ODAxIDMuNDU1OTYgMjIuNDgwMSAzLjk4ODYzQzIyLjQ4MDEgNC40NTczOCAyMi4zOTY3IDQuODQ0NDYgMjIuMjI5NyA1LjE0OTg1QzIyLjA2NjQgNS40NTUyNSAyMS44NDk4IDUuNjk2NzMgMjEuNTc5OSA1Ljg3NDI5QzIxLjMxMzYgNi4wNTE4NCAyMS4wMjQxIDYuMTgzMjMgMjAuNzExNiA2LjI2ODQ2VjYuMzc1QzIxLjA0NTQgNi4zOTYzIDIxLjM4MSA2LjUxMzQ5IDIxLjcxODQgNi43MjY1NkMyMi4wNTU3IDYuOTM5NjMgMjIuMzM4MSA3LjI0NTAyIDIyLjU2NTMgNy42NDI3NUMyMi43OTI2IDguMDQwNDggMjIuOTA2MiA4LjUyNjk4IDIyLjkwNjIgOS4xMDIyN0MyMi45MDYyIDkuNjQ5MTQgMjIuNzgyIDEwLjE0MSAyMi41MzM0IDEwLjU3NzhDMjIuMjg0OCAxMS4wMTQ2IDIxLjg5MjQgMTEuMzYwOCAyMS4zNTYyIDExLjYxNjVDMjAuODE5OSAxMS44NzIyIDIwLjEyMjIgMTIgMTkuMjYyOCAxMkgxNS4zMjFaTTE2LjY0MiAxMC44MjgxSDE5LjI2MjhDMjAuMTI1NyAxMC44MjgxIDIwLjczODMgMTAuNjYxMiAyMS4xMDA1IDEwLjMyNzRDMjEuNDY2MyA5Ljk5MDA1IDIxLjY0OTEgOS41ODE2NyAyMS42NDkxIDkuMTAyMjdDMjEuNjQ5MSA4LjczMjk1IDIxLjU1NSA4LjM5MjA0IDIxLjM2NjggOC4wNzk1NEMyMS4xNzg2IDcuNzYzNDkgMjAuOTEwNSA3LjUxMTM2IDIwLjU2MjUgNy4zMjMxNUMyMC4yMTQ1IDcuMTMxMzkgMTkuODAyNSA3LjAzNTUxIDE5LjMyNjcgNy4wMzU1MUgxNi42NDJWMTAuODI4MVpNMTYuNjQyIDUuODg0OTRIMTkuMDkyM0MxOS40OSA1Ljg4NDk0IDE5Ljg0ODcgNS44MDY4MSAyMC4xNjgzIDUuNjUwNTZDMjAuNDkxNSA1LjQ5NDMxIDIwLjc0NzIgNS4yNzQxNCAyMC45MzU0IDQuOTkwMDVDMjEuMTI3MSA0LjcwNTk2IDIxLjIyMyA0LjM3MjE2IDIxLjIyMyAzLjk4ODYzQzIxLjIyMyAzLjUwOTIzIDIxLjA1NjEgMy4xMDI2MiAyMC43MjIzIDIuNzY4ODJDMjAuMzg4NSAyLjQzMTQ2IDE5Ljg1OTQgMi4yNjI3OCAxOS4xMzQ5IDIuMjYyNzhIMTYuNjQyVjUuODg0OTRaIiBmaWxsPSIjRkUwNzA3Ii8+CjxwYXRoIGQ9Ik0xMS44Mzg4IDlMMTEuMzQ2NiA4LjUxNDJMMTMuMzcyOSA2LjQ4NzkySDguMTI0OTlWNS43ODQ4SDEzLjM3MjlMMTEuMzQ2NiAzLjc2NDkxTDExLjgzODggMy4yNzI3MkwxNC43MDI0IDYuMTM2MzZMMTEuODM4OCA5WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+'; 17 18 … … 43 44 } 44 45 $this->rag_controller = new S2bAia_AdminRagController(); 46 47 if (!class_exists('S2bAia_AdminUsageController')) { 48 $contr_path = S2BAIA_PATH . "/lib/controllers/AdminUsageController.php"; 49 include_once $contr_path; 50 } 51 $this->usage_controller = new S2bAia_AdminUsageController(); 45 52 46 53 add_action('admin_menu', array($this, 'registerAdminMenu')); … … 361 368 'showMainView' 362 369 ); 363 370 371 $this->registerSafeSubmenu( 372 __('Usage', 's2b-ai-assistant'), 373 S2BAIA_PREFIX_LOW . 'usage', 374 $this->usage_controller, 375 'showMainView' 376 ); 377 364 378 // Additional menu items if allowed 365 379 if (method_exists('S2bAia_Utils', 'checkEditInstructionAccess') && S2bAia_Utils::checkEditInstructionAccess()) { -
s2b-ai-assistant/trunk/lib/controllers/ChatBotController.php
r3338464 r3354820 785 785 if (S2bAia_AiRequest::testChatGptResponse($response)) { 786 786 $msg = S2bAia_AiRequest::getChatGptResponseEditMessage($response); 787 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 788 if($s2baia_use_usage){ 789 $usage = S2bAia_AiRequest::getUsage($response); 790 if (!class_exists('S2bAia_UsageUtils')) { 791 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 792 include_once $pp; 793 794 } 795 $user_id = get_current_user_id(); 796 $this->load_model('ChatBotModel'); 797 $model = $this->model; 798 $bot = $model->getChatBotSettings($bot_id); 799 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 800 S2bAia_UsageUtils::recordUsage($data['model'], $user_id, 1, $bot->id, $usage[0] , $usage[1]); 801 } 802 } 787 803 $r['result'] = 200; 788 804 $r['msg'] = wp_kses($msg, S2bAia_Utils::getInstructionAllowedTags()); … … 992 1008 $r['result'] = 200; 993 1009 $r['msg'] = wp_kses($msg, S2bAia_Utils::getInstructionAllowedTags()); 1010 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 1011 if ($s2baia_use_usage && is_object($response) && isset($response->usage) && is_object($response->usage) && isset($response->usage->prompt_tokens) && isset($response->usage->completion_tokens) ) { 1012 1013 if (!class_exists('S2bAia_UsageUtils')) { 1014 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 1015 include_once $pp; 1016 1017 } 1018 $inputtok = $response->usage->prompt_tokens; 1019 $outputtok = $response->usage->completion_tokens; 1020 $user_id = get_current_user_id(); 1021 $model = false; 1022 $path = S2BAIA_PATH . "/lib/models/ChatBotModel.php"; 1023 if (file_exists($path)) { 1024 $model_name = S2BAIA_CLASS_PREFIX . ucfirst('ChatBotModel'); 1025 1026 $model = new $model_name(); 1027 //ucfirst() 1028 } 1029 1030 $bot = $model->getChatBotSettings($bot_id); 1031 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 1032 S2bAia_UsageUtils::recordUsage(substr($data['model'],0,249), $user_id, 1, $bot->id, $inputtok , $outputtok); 1033 } 1034 } 994 1035 //$data['messages'][] = ['role'=>'assistant','content'=>$r['msg']]; 995 1036 $chat_id = $this->getChatId($bot_id); … … 1093 1134 } 1094 1135 1095 1136 $use_usage = get_option('s2baia_use_usage',0); 1096 1137 $response = false; 1097 1138 $answer_received = false; … … 1111 1152 } 1112 1153 if (strlen($message_id) > 0) { 1113 $response = S2bAia_AiRequest::runAssistant($thread_id, $assistant_id, '' );1154 $response = S2bAia_AiRequest::runAssistant($thread_id, $assistant_id, '',$use_usage); 1114 1155 $this->debugLog(wp_json_encode($response), 0, 'runAssistant thread-'.$thread_id.' assistant - '.$assistant_id, $assistant_id, $chat_id); 1115 1156 if (is_array($response) && isset($response['id']) && isset($response['status'])) { … … 1120 1161 $chb_model->updateChat($chat_id, $last_msg, $chat_info, $last_role, $this->chat_session_expired); 1121 1162 $this->updateLog($last_msg, $chat_info, $last_role, $bot_id, $chat_id, 2); 1163 1164 $usage = $response['_usage'] ?? null; 1165 if($usage && $use_usage){ 1166 1167 if (!class_exists('S2bAia_UsageUtils')) { 1168 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 1169 include_once $pp; 1170 1171 } 1172 $user_id = get_current_user_id(); 1173 $this->load_model('ChatBotModel'); 1174 $model = $this->model; 1175 $bot = $model->getChatBotSettings($bot_id); 1176 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 1177 $in_tokens = (int)($usage['prompt_tokens'] ?? 0); 1178 $out_tokens = (int)($usage['completion_tokens'] ?? 0); 1179 1180 S2bAia_UsageUtils::recordUsage('AI Assistant API Async', $user_id, 1, $bot->id, $in_tokens , $out_tokens); 1181 } 1182 } 1183 1122 1184 if ($status == 'queued') { 1123 while ($status != 'completed' || $count_loop > 0) { 1185 // Use AND here, so the loop stops when completed OR when timeout runs out 1186 while ($status !== 'completed' && $count_loop > 0) { 1124 1187 $response = S2bAia_AiRequest::getRunStepsStatus($thread_id, $run_id); 1125 1188 $this->debugLog(wp_json_encode($response), 0, 'getRunStepsStatus threaid:'.$thread_id.' runid:'.$run_id, $assistant_id, $chat_id); 1189 1126 1190 if (is_array($response) && isset($response['data'])) { 1127 1191 $list = $response['data']; 1128 1192 foreach ($list as $ls) { 1129 if ($ls['status'] == 'completed') { 1193 if ($ls['status'] === 'completed') { 1194 // ✅ Steps are completed; now the run should be terminal soon or already 1195 // 1) Fetch final run to get usage 1196 $finalRun = S2bAia_AiRequest::getRun($thread_id, $run_id); 1197 $this->debugLog(wp_json_encode($finalRun), 0, 'finalRun (for usage)', $assistant_id, $chat_id); 1198 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 1199 if($s2baia_use_usage){ 1200 $usage = is_array($finalRun) ? ($finalRun['usage'] ?? null) : null; 1201 if ($usage && is_array($usage)) { 1202 // Record usage once per run 1203 if (!class_exists('S2bAia_UsageUtils')) { 1204 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 1205 include_once $pp; 1206 } 1207 $user_id = get_current_user_id(); 1208 $this->load_model('ChatBotModel'); 1209 $model = $this->model; 1210 $bot = $model->getChatBotSettings($bot_id); 1211 1212 if (is_object($bot) && !empty($bot->id)) { 1213 $in_tokens = (int)($usage['prompt_tokens'] ?? 0); 1214 $out_tokens = (int)($usage['completion_tokens'] ?? 0); 1215 S2bAia_UsageUtils::recordUsage('AI Assistant API Async', $user_id, 1, $bot->id, $in_tokens, $out_tokens); 1216 } 1217 } 1218 } 1219 // 2) Now fetch messages 1130 1220 sleep(1); 1131 1221 $response = S2bAia_AiRequest::listAssistantMessages($thread_id); … … 1134 1224 } 1135 1225 } elseif ($response === true) { 1136 1226 // code path for "steps done" shortcut 1227 // 1) Fetch final run usage here too 1228 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 1229 if($s2baia_use_usage){ 1230 $finalRun = S2bAia_AiRequest::getRun($thread_id, $run_id); 1231 $this->debugLog(wp_json_encode($finalRun), 0, 'finalRun (for usage) [shortcut]', $assistant_id, $chat_id); 1232 } 1233 if($s2baia_use_usage){ 1234 $usage = is_array($finalRun) ? ($finalRun['usage'] ?? null) : null; 1235 if ($usage && is_array($usage)) { 1236 if (!class_exists('S2bAia_UsageUtils')) { 1237 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 1238 include_once $pp; 1239 } 1240 $user_id = get_current_user_id(); 1241 $this->load_model('ChatBotModel'); 1242 $model = $this->model; 1243 $bot = $model->getChatBotSettings($bot_id); 1244 1245 if (is_object($bot) && !empty($bot->id)) { 1246 $in_tokens = (int)($usage['prompt_tokens'] ?? 0); 1247 $out_tokens = (int)($usage['completion_tokens'] ?? 0); 1248 S2bAia_UsageUtils::recordUsage('AI Assistant API Async', $user_id, 1, $bot->id, $in_tokens, $out_tokens); 1249 } 1250 } 1251 } 1252 // 2) Then fetch messages as already do 1137 1253 sleep(1); 1138 1254 $response = S2bAia_AiRequest::listAssistantMessages($thread_id); 1139 $fl2=__DIR__."/response_async_assistant.txt";1140 $logvar = $response ;1141 //error_log(print_r($logvar,true),3,$fl2);1142 1255 $this->debugLog(wp_json_encode($response), 0, 'listAssistantMessages2 threadid:'.$thread_id, $assistant_id, $chat_id); 1256 1143 1257 $parsed_response = $this->parseListResponse($response); 1144 if (strlen($parsed_response['text']) > 0 && $parsed_response['text'] !== '**empty' && $parsed_response['text'] !== '**echoed'){1145 $final_response['msg'] = $parsed_response['text'];1258 if (strlen($parsed_response['text']) > 0 && $parsed_response['text'] !== '**empty' && $parsed_response['text'] !== '**echoed') { 1259 $final_response['msg'] = $parsed_response['text']; 1146 1260 $final_response['code'] = 200; 1147 1261 $answer_received = true; … … 1150 1264 $this->updateLog($parsed_response['text'], $chat_info, 'assistant', $bot_id, $chat_id, 1); 1151 1265 break; 1152 } elseif(strlen($parsed_response['text']) > 0 && $parsed_response['text'] == '**echoed'){1266 } elseif (strlen($parsed_response['text']) > 0 && $parsed_response['text'] == '**echoed') { 1153 1267 $response = S2bAia_AiRequest::retryRequest(); 1154 1268 $this->debugLog(wp_json_encode($response), 0, 'retryRequest:'.$thread_id, $assistant_id, $chat_id); 1155 if (isset($response) && is_string($response)){1156 $final_response['msg'] = $response;1269 if (isset($response) && is_string($response)) { 1270 $final_response['msg'] = $response; 1157 1271 $final_response['code'] = 200; 1158 1272 $answer_received = true; … … 1163 1277 } 1164 1278 } 1165 //$logvar = '**empty' ; 1166 //error_log(print_r($logvar,true)); 1167 }else{ 1168 $final_response['msg'] = esc_html__('Can not establish connection to assistant. Please send your request again.', 's2b-ai-assistant').' '.esc_html__('Error code', 's2b-ai-assistant').':'.'409'; 1279 } else { 1280 $final_response['msg'] = esc_html__('Can not establish connection to assistant. Please send your request again.', 's2b-ai-assistant').' '.esc_html__('Error code', 's2b-ai-assistant').':'.'409'; 1169 1281 $final_response['code'] = 409; 1170 1282 } 1283 1171 1284 $count_loop--; 1172 1285 sleep(1); // gentle pacing 1173 1286 } 1174 } else{1175 $final_response['msg'] = esc_html__('Can not establish connection to assistant. Please send your request again.', 's2b-ai-assistant').' '.esc_html__('Error code', 's2b-ai-assistant').':'.'406';1287 } else { 1288 $final_response['msg'] = esc_html__('Can not establish connection to assistant. Please send your request again.', 's2b-ai-assistant').' '.esc_html__('Error code', 's2b-ai-assistant').':'.'406'; 1176 1289 $final_response['code'] = 406; 1177 1290 } … … 1303 1416 $debugger = S2bAia_Utils::$global_logger; 1304 1417 $debug_log = (get_option( 's2baia_debug', 0 ) > 0) && is_object($debugger) && strlen(S2bAia_Utils::$log_id) > 0; 1305 1418 1419 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 1420 if($s2baia_use_usage && is_array($response_stream) && count($response_stream) > 3 && is_array($response_stream[3]) && isset($response_stream[3]['prompt_tokens']) && isset($response_stream[3]['completion_tokens'])){ 1421 $usage = $response_stream[3]; 1422 if (!class_exists('S2bAia_UsageUtils')) { 1423 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 1424 include_once $pp; 1425 1426 } 1427 $user_id = get_current_user_id(); 1428 $this->load_model('ChatBotModel'); 1429 $model = $this->model; 1430 $bot = $model->getChatBotSettings($bot_id); 1431 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 1432 $in_tokens = $usage['prompt_tokens'] ?? 0; 1433 $out_tokens = $usage['completion_tokens'] ?? 0; 1434 S2bAia_UsageUtils::recordUsage('AI Assistant API Stream', $user_id, 1, $bot->id, $in_tokens , $out_tokens); 1435 } 1436 } 1306 1437 //error_log(print_r($logvar,true),3,$fl2); 1307 1438 if(is_array($response_stream) && count($response_stream) > 2){ -
s2b-ai-assistant/trunk/lib/helpers/AiRequest.php
r3342891 r3354820 15 15 public static $thread_url = "https://api.openai.com/v1/threads"; 16 16 public static $http_client = 'curl'; 17 17 public static $default_headers = null; 18 18 19 /* prepares chat completion chatGPT API request and calls :sendChatGptRequest 19 20 @param $data array received from Edit&Extend metabox form … … 214 215 return ''; 215 216 } 217 218 public static function getUsage($response) { 219 $usage = [0, 0]; 220 if (is_object($response) && isset($response->usage) && is_object($response->usage) && isset($response->usage->prompt_tokens)) { 221 $usage[0] = $prompt_tokens = $response->usage->prompt_tokens; 222 $usage[1] = $completion_tokens = $response->usage->completion_tokens; //completion_tokens 223 224 } 225 return $usage; 226 } 227 216 228 217 229 public static function createAssistantRetrievalV2($options) { … … 671 683 } 672 684 673 public static function runAssistant ($thread_id, $assistant_id, $instruction) {685 public static function runAssistantV1($thread_id, $assistant_id, $instruction) { 674 686 675 687 $url = self::$thread_url . "/" . $thread_id . "/runs"; … … 721 733 } 722 734 735 private static function oa_headers($api_key = null) { 736 $key = $api_key ?: (self::$gpt_key ?: get_option(S2BAIA_PREFIX_LOW . 'open_ai_gpt_key', '')); 737 return [ 738 'Content-Type' => 'application/json', 739 'Authorization' => 'Bearer ' . $key, 740 'OpenAI-Beta' => 'assistants=v2', 741 ]; 742 } 743 744 // Small helper: fetch a single run 745 public static function getRun($thread_id, $run_id, $api_key = null) { 746 $url = "https://api.openai.com/v1/threads/{$thread_id}/runs/{$run_id}"; 747 $resp = wp_remote_get($url, [ 748 'headers' => self::oa_headers($api_key), 749 'timeout' => 60, 750 ]); 751 if (is_wp_error($resp)) { 752 return ['error' => $resp->get_error_message()]; 753 } 754 return json_decode(wp_remote_retrieve_body($resp), true); 755 } 756 757 // Wait/poll until run reaches a terminal state or timeout 758 private static function waitForRunUsage($thread_id, $run_id, $api_key = null, $max_wait_sec = 60) { 759 $started = time(); 760 $sleep = 0.6; // start interval 761 $max_sleep = 2.5; // cap interval 762 763 while (true) { 764 $run = self::getRun($thread_id, $run_id, $api_key); 765 if (!is_array($run)) { 766 return ['error' => 'Unexpected run payload']; 767 } 768 $status = $run['status'] ?? 'unknown'; 769 770 // Terminal states 771 if (in_array($status, ['completed', 'failed', 'cancelled', 'expired'], true)) { 772 return [ 773 'status' => $status, 774 'usage' => $run['usage'] ?? null, 775 'model' => $run['model'] ?? null, 776 'run' => $run, 777 ]; 778 } 779 780 // Timeout? 781 if ((time() - $started) >= $max_wait_sec) { 782 return [ 783 'status' => $status, 784 'usage' => $run['usage'] ?? null, 785 'model' => $run['model'] ?? null, 786 'timeout' => true, 787 'run' => $run, 788 ]; 789 } 790 791 // Backoff then continue 792 usleep((int)($sleep * 1000)); 793 $sleep = min($max_sleep, $sleep * 1.4); 794 } 795 } 796 797 public static function runAssistant($thread_id, $assistant_id, $instruction, $collect_usage = true, $max_wait_sec = 60) { 798 799 $url = self::$thread_url . "/" . $thread_id . "/runs"; 800 if (strlen(self::$gpt_key) == 0) { 801 self::$gpt_key = get_option(S2BAIA_PREFIX_LOW . 'open_ai_gpt_key', ''); 802 } 803 804 $data = (strlen($instruction) > 1) 805 ? ["assistant_id" => $assistant_id, "instructions" => $instruction] 806 : ["assistant_id" => $assistant_id]; 807 808 $headers = self::oa_headers(self::$gpt_key); 809 810 $response = wp_remote_request($url, [ 811 'method' => 'POST', 812 'headers' => $headers, 813 'body' => wp_json_encode($data), 814 'timeout' => 120, 815 ]); 816 817 $res = []; 818 819 if (is_wp_error($response)) { 820 $res['error_msg'] = $response->get_error_message(); 821 $res['code'] = 500; 822 return $res; 823 } 824 825 $res['code'] = wp_remote_retrieve_response_code($response); 826 $res['body'] = wp_remote_retrieve_body($response); 827 828 if (!strlen($res['body'])) { 829 return $res; 830 } 831 832 $res_obj = json_decode($res['body'], true); 833 // Return early if caller doesn't want blocking usage calc 834 if (!$collect_usage) { 835 // Add convenience fields without changing old shape 836 if (is_array($res_obj)) { 837 $res_obj['_run_id'] = $res_obj['id'] ?? null; 838 $res_obj['_status'] = $res_obj['status']?? null; 839 $res_obj['_usage'] = $res_obj['usage'] ?? null; // rarely present at creation 840 } 841 return $res_obj; 842 } 843 844 // Collect usage: poll until completed (or timeout) 845 $run_id = $res_obj['id'] ?? null; 846 if (!$run_id) { 847 // Unexpected; return original payload 848 $res_obj['_usage_error'] = 'Missing run_id in creation response'; 849 return $res_obj; 850 } 851 852 $final = self::waitForRunUsage($thread_id, $run_id, self::$gpt_key, $max_wait_sec); 853 854 // Attach usage + status back to the original object (non-breaking) 855 $res_obj['_run_id'] = $run_id; 856 $res_obj['id'] = $run_id; 857 $res_obj['_status'] = $final['status'] ?? null; 858 $res_obj['_usage'] = $final['usage'] ?? null; // ['prompt_tokens','completion_tokens','total_tokens'] 859 $res_obj['_model'] = $final['model'] ?? null; 860 $res_obj['_timeout'] = $final['timeout']?? false; 861 862 return $res_obj; 863 } 864 865 723 866 public static function uploadFile($file_path) { 724 867 $http_client = self::$http_client; … … 909 1052 require_once S2BAIA_PATH . '/lib/helpers/CurlClient.php'; 910 1053 } 911 $response = S2bAia_CurlClient::openStream($url, $options );1054 $response = S2bAia_CurlClient::openStream($url, $options,$thread_id); 912 1055 913 1056 if ($response === FALSE) { … … 958 1101 $args_f = apply_filters( 's2baia_chat_assistant_stream_filter_args', $args, $thread_id, $assistant_id, $instruction ); 959 1102 // Call the WordPress-based openStream function 960 return S2bAia_WpHttpClient::openStream($url_f, $args_f );1103 return S2bAia_WpHttpClient::openStream($url_f, $args_f,$thread_id); 961 1104 } 962 1105 } -
s2b-ai-assistant/trunk/lib/helpers/UpdateUtils.php
r3292097 r3354820 36 36 if($current_db_version < 7){ 37 37 self::version7(); 38 update_option('s2baia_config_pinecone_system_message', esc_html__('Answer the question based on the context below','s2b-ai-assistant'));38 update_option('s2baia_config_pinecone_system_message', 'Answer the question based on the context below','s2b-ai-assistant'); 39 39 update_option('s2baia_database_version', 7); 40 40 } … … 55 55 } 56 56 57 if($current_db_version < 11){ 58 self::version11(); 59 update_option('s2baia_database_version', 11); 60 } 57 61 } 58 62 … … 831 835 'chat_height_metrics'=>'%', 832 836 'greeting_message' => 0, 833 'greeting_message_text'=> esc_html__('Hello! I am an AI Assistant. How can I help you?','s2b-ai-assistant'),834 'message_placeholder'=> esc_html__('Ctrl+Enter to send request','s2b-ai-assistant'),837 'greeting_message_text'=>'Hello! I am an AI Assistant. How can I help you?', 838 'message_placeholder'=>'Ctrl+Enter to send request', 835 839 'chatbot_name'=>'GPT Assistant', 836 840 'compliance_text'=>'', … … 854 858 'access_for_guests'=> 1, 855 859 'chatbot_picture_url' => '', 856 'send_button_text' => esc_html__('Send','s2b-ai-assistant'),857 'clear_button_text' => esc_html__('Clear','s2b-ai-assistant')860 'send_button_text' => 'Send', 861 'clear_button_text' => 'Clear' 858 862 ]; 859 863 … … 998 1002 } 999 1003 1000 1004 /*public static function version11() { 1005 global $wpdb; 1006 1007 $table_name = $wpdb->prefix . 's2baia_usage'; 1008 $charset_collate = $wpdb->get_charset_collate(); 1009 1010 $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( 1011 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 1012 type_of_resource SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1, 1013 id_resource VARCHAR(191) NOT NULL, 1014 id_user BIGINT(20) UNSIGNED NOT NULL, 1015 resource_session_id VARCHAR(191) NOT NULL DEFAULT '', 1016 model VARCHAR(191) NOT NULL DEFAULT '', 1017 date_updated DATE DEFAULT NULL, 1018 time_updated INT(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'seconds within date', 1019 input_tokens BIGINT(20) UNSIGNED DEFAULT NULL, 1020 output_tokens BIGINT(20) UNSIGNED DEFAULT NULL, 1021 details LONGTEXT, 1022 PRIMARY KEY (id), 1023 1024 KEY type_resource_user ( 1025 type_of_resource, 1026 id_resource(120), 1027 id_user, 1028 model(60) 1029 ), 1030 KEY type_resource_user_session ( 1031 type_of_resource, 1032 id_resource(120), 1033 id_user, 1034 resource_session_id(60) 1035 ), 1036 KEY type_resource_user_date ( 1037 type_of_resource, 1038 id_resource(120), 1039 id_user, 1040 model(60), 1041 date_updated 1042 ) 1043 ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC $charset_collate;"; 1044 1045 1046 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 1047 dbDelta($sql); 1048 }*/ 1049 1050 public static function version11() { 1051 self::create_usage_table(); 1052 update_option('s2baia_database_version', 11); 1053 self::ensure_usage_indexes(); 1054 } 1055 1056 private static function create_usage_table() { 1057 global $wpdb; 1058 $table = $wpdb->prefix . 's2baia_usage'; 1059 $charset_collate = $wpdb->get_charset_collate(); 1060 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 1061 1062 // Base table only: columns + PK (no secondary keys yet) 1063 $sql = "CREATE TABLE IF NOT EXISTS {$table} ( 1064 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 1065 type_of_resource SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1, 1066 id_resource VARCHAR(191) NOT NULL, 1067 id_user BIGINT(20) UNSIGNED NOT NULL, 1068 resource_session_id VARCHAR(191) NOT NULL DEFAULT '', 1069 model VARCHAR(191) NOT NULL DEFAULT '', 1070 date_updated DATE DEFAULT NULL, 1071 time_updated INT(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'seconds within date', 1072 input_tokens BIGINT(20) UNSIGNED DEFAULT NULL, 1073 output_tokens BIGINT(20) UNSIGNED DEFAULT NULL, 1074 details LONGTEXT, 1075 PRIMARY KEY (id) 1076 ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC {$charset_collate};"; 1077 1078 $wpdb->suppress_errors(false); 1079 dbDelta($sql); 1080 1081 if (!empty($wpdb->last_error)) { 1082 //error_log('[s2baia] CREATE TABLE error: ' . $wpdb->last_error); 1083 } 1084 } 1085 1086 private static function ensure_usage_indexes() { 1087 global $wpdb; 1088 $table = $wpdb->prefix . 's2baia_usage'; 1089 1090 // Desired indexes as arrays of [col, optional_prefix] 1091 // We’ll try full columns first; on failure we retry with prefixes that are 1000-byte safe. 1092 $wanted = [ 1093 'type_resource_user' => [ 1094 ['type_of_resource', null], 1095 ['id_resource', null], // may get prefixed if key-too-long 1096 ['id_user', null], 1097 ['model', null], // may get prefixed if key-too-long 1098 ], 1099 'type_resource_user_session' => [ 1100 ['type_of_resource', null], 1101 ['id_resource', null], // prefixed fallback 1102 ['id_user', null], 1103 ['resource_session_id', null], // prefixed fallback 1104 ], 1105 'type_resource_user_date' => [ 1106 ['type_of_resource', null], 1107 ['id_resource', null], // prefixed fallback 1108 ['id_user', null], 1109 ['model', null], // prefixed fallback 1110 ['date_updated', null], 1111 ], 1112 ]; 1113 1114 foreach ($wanted as $indexName => $cols) { 1115 self::maybe_add_index($table, $indexName, $cols); 1116 } 1001 1117 } 1002 1118 1119 private static function usage_index_exists($indexName) { 1120 global $wpdb; 1121 return (bool) $wpdb->get_var(/* phpcs:ignore WordPress.DB.DirectDatabaseQuery */ 1122 $wpdb->prepare("SHOW INDEX FROM {$wpdb->prefix}s2baia_usage WHERE Key_name = %s", $indexName) 1123 ); 1124 } 1125 1126 /** 1127 * Backtick-quote a MySQL identifier after strict validation. 1128 */ 1129 private static function quote_ident(string $name): string { 1130 // allow only letters, numbers, underscore, and $ 1131 if (!preg_match('/^[A-Za-z0-9_\$]+$/', $name)) { 1132 throw new InvalidArgumentException("Invalid identifier"); 1133 } 1134 return '`' . $name . '`'; 1135 } 1136 1137 /** 1138 * Add an INDEX with optional prefix lengths (for text columns). 1139 * $colsSpec is an array of [columnName, prefixLen|null] 1140 */ 1141 private static function try_add_index($table, $indexName, $colsSpec) { 1142 global $wpdb; 1143 1144 // optional: hard whitelist of columns that exist in this table 1145 static $allowed_cols = [ 1146 'type_of_resource', 'id_resource', 'id_user', 1147 'resource_session_id', 'model', 'date_updated' 1148 ]; 1149 1150 $parts = []; 1151 foreach ($colsSpec as [$col, $prefix]) { 1152 if (!in_array($col, $allowed_cols, true)) { 1153 throw new InvalidArgumentException("Unknown column for index"); 1154 } 1155 $colSql = self::quote_ident($col) . ($prefix ? '(' . (int)$prefix . ')' : ''); 1156 $parts[] = $colSql; 1157 } 1158 1159 $tableSql = self::quote_ident($table); 1160 $indexSql = self::quote_ident($indexName); 1161 $colsSql = implode(', ', $parts); 1162 $sql = "ALTER TABLE {$tableSql} ADD INDEX {$indexSql} ({$colsSql})"; 1163 1164 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifiers cannot be parameterized; validated & quoted above. 1165 $wpdb->query($sql); 1166 1167 return empty($wpdb->last_error); 1168 } 1169 1170 1171 private static function maybe_add_index($table, $indexName, $cols) { 1172 1173 global $wpdb; 1174 if (self::usage_index_exists($indexName)) return; 1175 1176 $wpdb->suppress_errors(false); 1177 1178 // 1) Try full-length first. 1179 if (self::try_add_index($table, $indexName, $cols)) return; 1180 1181 $err1 = $wpdb->last_error; 1182 // 2) If key-too-long (or anything else), retry with safe prefixes for text columns under utf8mb4. 1183 // Rule: keep total <= 1000 bytes on older hosts. Use conservative (120, 60) prefixes. 1184 $bytesPerChar = (stripos($wpdb->charset, 'utf8mb4') !== false) ? 4 : 3; 1185 1186 $prefixed = []; 1187 foreach ($cols as [$col, $prefix]) { 1188 if ($prefix !== null) { // user-forced prefix (not used above, but kept flexible) 1189 $prefixed[] = [$col, $prefix]; 1190 continue; 1191 } 1192 // Heuristic: prefix VARCHAR/TEXT-like columns, not numeric/date types. 1193 // Ask MySQL about the column type: 1194 $type = $wpdb->get_var($wpdb->prepare( 1195 "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS 1196 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = %s", 1197 $table, $col 1198 )); 1199 $is_texty = in_array(strtolower((string)$type), ['varchar','text','mediumtext','longtext','tinytext']); 1200 1201 if ($is_texty) { 1202 // conservative default prefixes for composites under legacy 1000-byte limit 1203 // 120 chars ≈ 480 bytes on utf8mb4 1204 $prefixed[] = [$col, 120]; 1205 } else { 1206 $prefixed[] = [$col, null]; 1207 } 1208 } 1209 1210 // Refine: if multiple text columns, make the later ones smaller (e.g., 60) 1211 $textIdx = 0; 1212 foreach ($prefixed as $i => [$col, $prefix]) { 1213 if ($prefix) { 1214 $prefixed[$i][1] = ($textIdx === 0) ? 120 : 60; // first text col 120, next ones 60 1215 $textIdx++; 1216 } 1217 } 1218 1219 // Try again with prefixes 1220 $wpdb->last_error = ''; 1221 if (self::try_add_index($table, $indexName, $prefixed)) { 1222 return; 1223 } 1224 1225 /*error_log(sprintf( 1226 '[s2baia] Failed to add index %s to %s. First error: %s ; Prefixed attempt error: %s ; Attempted spec: %s', 1227 $indexName, $table, $err1, $wpdb->last_error, json_encode($prefixed) 1228 ));*/ 1229 } 1230 1231 1232 } 1233 1003 1234 } -
s2b-ai-assistant/trunk/lib/helpers/WpHttpClient.php
r3292097 r3354820 10 10 public static $annotations = []; 11 11 12 public static $run_id = null; 13 public static $run_usage = null; 14 12 15 public static function sendWpRequest($url, $args) { 13 16 $response = wp_remote_request($url, $args); … … 30 33 if (!empty($data)) { 31 34 $obj = json_decode($data, true); 32 35 if (!$obj) { 36 return $data; // keep old behavior 37 } 38 // --- NEW: unwrap Assistants v2 SSE envelope if present --------------- 39 // Many lines look like: {"event":"thread.run.created","data":{...}} 40 $event = $obj['event'] ?? null; 41 $payload = $obj['data'] ?? null; 42 if ($event && is_array($payload)) { 43 // Capture run_id / usage from run lifecycle events 44 switch ($event) { 45 case 'thread.run.created': 46 case 'thread.run.in_progress': 47 case 'thread.run.queued': 48 case 'thread.run.requires_action': 49 case 'thread.run.completed': 50 case 'thread.run.failed': 51 case 'thread.run.cancelled': 52 case 'thread.run.expired': 53 if (!empty($payload['id'])) { 54 self::$run_id = $payload['id']; 55 } 56 if (!empty($payload['usage']) && is_array($payload['usage'])) { 57 // usage = { prompt_tokens, completion_tokens, total_tokens } 58 self::$run_usage = $payload['usage']; 59 } 60 break; 61 } 62 63 // For downstream legacy logic, operate on the inner payload 64 $obj = $payload; 65 } 66 67 // --- NEW: also catch bare run objects (in case stream sends them) ---- 68 if (($obj['object'] ?? '') === 'thread.run') { 69 if (!empty($obj['id'])) { 70 self::$run_id = $obj['id']; 71 } 72 if (!empty($obj['usage']) && is_array($obj['usage'])) { 73 self::$run_usage = $obj['usage']; 74 } 75 } 76 77 78 33 79 if (!class_exists('S2bAia_FileExtender')) { 34 80 require_once S2BAIA_PATH . '/lib/classes/FileExtender.php'; … … 145 191 146 192 147 public static function openStream ($url, $args) {193 public static function openStreamFast($url, $args) { 148 194 self::$stream_answer = ''; // Reset buffer 195 // 149 196 // Make request 150 197 $response = wp_remote_request($url, $args); … … 166 213 return [self::$stream_answer, self::$annotations, self::$thread_message]; // Return final accumulated response 167 214 } 215 216 public static function openStream($url, $args, $thread_id) { 217 self::$stream_answer = ''; 218 self::$annotations = []; 219 self::$thread_message = null; 220 self::$run_id = null; 221 self::$run_usage = null; 222 223 $response = wp_remote_request($url, $args); 224 if (is_wp_error($response)) { 225 return [["error" => $response->get_error_message()], false]; 226 } 227 228 $body = wp_remote_retrieve_body($response); 229 $lines = explode("\n", $body); 230 231 foreach ($lines as $line) { 232 if (strpos($line, 'data:') === 0) { 233 $data = trim(substr($line, strlen('data:'))); 234 self::handleEvent($data); // will fill answer + run_id + usage 235 } 236 } 237 238 // If usage wasn't present in the stream, fetch it from the final run 239 if (!self::$run_usage && self::$run_id) { 240 $api_key = get_option(S2BAIA_PREFIX_LOW . 'open_ai_gpt_key', ''); 241 $usage = self::fetchRunUsage($thread_id, self::$run_id, $api_key); 242 if ($usage) { 243 self::$run_usage = $usage; 244 } 245 } 246 247 // Return also the usage + run_id so you can record cost 248 return [self::$stream_answer, self::$annotations, self::$thread_message, self::$run_usage, self::$run_id]; 249 } 250 251 // Separate GET to fetch run usage if the stream didn’t include it 252 public static function fetchRunUsage($thread_id, $run_id, $api_key) { 253 $url = "https://api.openai.com/v1/threads/$thread_id/runs/$run_id"; 254 $resp = wp_remote_get($url, [ 255 'headers' => [ 256 'Authorization' => 'Bearer ' . $api_key, 257 'OpenAI-Beta' => 'assistants=v2', // keep if you’re on Assistants v2 258 'Content-Type' => 'application/json', 259 ], 260 'timeout' => 60, 261 ]); 262 263 if (is_wp_error($resp)) return null; 264 265 $body = json_decode(wp_remote_retrieve_body($resp), true); 266 return $body['usage'] ?? null; 267 } 168 268 169 269 public static function generateDownloadLink($file_id) { -
s2b-ai-assistant/trunk/lib/integrations/anthropic/Anthropic.php
r3338465 r3354820 297 297 298 298 if(empty($provider) || $provider != 'anthropic'){ 299 if(is_array($result) && isset($result['msg']) && isset($result['code']) && isset($result['result']) ){ 300 return $result; 301 } 299 302 return ['msg'=>'','code'=>404,'result'=>404]; 300 303 } … … 585 588 //$returned_model = $decoded_response['model']; 586 589 //$usage = $decoded_response['usage']; 590 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 591 592 if($s2baia_use_usage && is_array($decoded_response) && isset($decoded_response['usage'])){ 593 $usage = $decoded_response['usage'];//$selected_model 594 $intok = 0; 595 $outok = 0; 596 if(is_array($usage) && isset($usage['input_tokens']) ){ 597 $intok = (int)$usage['input_tokens']; 598 } 599 if(is_array($usage) && isset($usage['output_tokens']) ){// 600 $outok = (int)$usage['output_tokens']; 601 } 602 if (!class_exists('S2bAia_UsageUtils')) { 603 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 604 include_once $pp; 605 606 } 607 $user_id = get_current_user_id(); 608 $model = false; 609 $path = S2BAIA_PATH . "/lib/models/ChatBotModel.php"; 610 if (file_exists($path)) { 611 $model_name = S2BAIA_CLASS_PREFIX . ucfirst('ChatBotModel'); 612 613 $model = new $model_name(); 614 //ucfirst() 615 } 616 617 $bot = $model->getChatBotSettings($bot_id); 618 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 619 S2bAia_UsageUtils::recordUsage(substr($selected_model,0,249), $user_id, 1, $bot->id, $intok , $outok); 620 } 621 622 } 587 623 $text_content = ''; 588 624 if (isset($decoded_response['content'])) { -
s2b-ai-assistant/trunk/lib/integrations/deepseek/DeepSeek.php
r3338464 r3354820 251 251 252 252 if(empty($provider) || $provider != 'deepseek'){ 253 if(is_array($result) && isset($result['msg']) && isset($result['code']) && isset($result['result']) ){ 254 return $result; 255 } 253 256 return ['msg'=>'','code'=>404,'result'=>404]; 254 257 } … … 516 519 $response_body = wp_remote_retrieve_body($response); 517 520 $decoded_response = json_decode($response_body, true); 518 521 $s2baia_use_usage = get_option('s2baia_use_usage', 0); 522 if ($s2baia_use_usage && isset($decoded_response['usage'])) { 523 if (is_array($decoded_response['usage']) && isset($decoded_response['usage']['prompt_tokens']) && isset($decoded_response['usage']['completion_tokens'])) { 524 if (!class_exists('S2bAia_UsageUtils')) { 525 $pp = S2BAIA_PATH . '/lib/helpers/UsageUtils.php'; 526 include_once $pp; 527 528 } 529 $user_id = get_current_user_id(); 530 $model = false; 531 $path = S2BAIA_PATH . "/lib/models/ChatBotModel.php"; 532 if (file_exists($path)) { 533 $model_name = S2BAIA_CLASS_PREFIX . ucfirst('ChatBotModel'); 534 $model = new $model_name(); 535 //ucfirst() 536 } 537 538 $bot = $model->getChatBotSettings($bot_id); 539 if(is_object($bot) && isset($bot->id) && $bot->id > 0){ 540 S2bAia_UsageUtils::recordUsage($selected_model, $user_id, 1, $bot->id, $decoded_response['usage']['prompt_tokens'] , $decoded_response['usage']['completion_tokens']); 541 } 542 } 543 } 519 544 if (isset($decoded_response['choices'][0]['message']['content'])) { 520 545 return [ -
s2b-ai-assistant/trunk/readme.txt
r3342891 r3354820 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 Stable tag: 1.7. 510 Stable tag: 1.7.6 11 11 12 12 Create multiple AI chatbots with OpenAI, Claude, xAI, DeepSeek models with different styles and behavior, content aware features ... … … 266 266 == Changelog == 267 267 268 = 1.7.6 = 269 * Fix integrations conflict. Add tokens usage statistic 270 268 271 = 1.7.5 = 269 272 * Quick fix to add basic GPT-5 model support. Only chat completion API. Full support is coming. -
s2b-ai-assistant/trunk/s2b-ai-assistant.php
r3342891 r3354820 8 8 Text Domain: s2b-ai-assistant 9 9 Domain Path: /lang 10 Version: 1.7. 510 Version: 1.7.6 11 11 License: GPL-2.0+ 12 12 License URI: http://www.gnu.org/licenses/gpl-2.0.txt … … 43 43 define( 'S2BAIA_CHATGPT_BOT_PREFIX', 's2baia_chatbot_' ); 44 44 define( 'S2BAIA_CHATGPT_BOT_OPTIONS_PREFIX', 's2baia_chatbot_opt_' ); 45 define('S2BAIA_VERSION', '1.7. 4');45 define('S2BAIA_VERSION', '1.7.6'); 46 46 //Init the plugin 47 47 require_once S2BAIA_PATH . '/lib/helpers/Utils.php'; -
s2b-ai-assistant/trunk/views/backend/chatbot/chatbot_general.php
r3338464 r3354820 774 774 </div> 775 775 </div> 776 777 778 <div class="s2baia_block_content" > 779 <div class="s2baia_row_header"> 780 <label for="s2baia_chatbot_use_usage"> 781 <?php esc_html_e('Log Usage', 's2b-ai-assistant'); ?>: 782 </label> 783 </div> 784 <div class="s2baia_row_content s2baia_pr"> 785 <div style="position:relative;"> 786 <?php 787 $checked = ''; 788 $usage_used = get_option('s2baia_use_usage', 0); 789 if ($usage_used == 1) { 790 $checked = ' checked '; 791 } 792 ?> 793 794 <input type="checkbox" id="s2baia_chatbot_use_usage" 795 name="s2baia_chatbot_use_usage" 796 <?php echo esc_html($checked); ?> > 797 798 </div> 799 <p class="s2baia_input_description"> 800 <span style="display: inline;"> 801 <?php esc_html_e('Check this if you want to collect statistic of tokens usage by chatbots', 's2b-ai-assistant'); ?> 802 </span> 803 </p> 804 </div> 805 </div> 776 806 <?php 777 807 }
Note: See TracChangeset
for help on using the changeset viewer.