PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.4.7
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.4.7
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 / services / usage-stats.php
ai-engine / classes / services Last commit date
image.php 7 months ago message-builder.php 8 months ago model-environment.php 7 months ago response-id-manager.php 3 months ago session.php 11 months ago usage-stats.php 2 months ago
usage-stats.php
448 lines
1 <?php
2
3 class Meow_MWAI_Services_UsageStats {
4 private $core;
5 private $tiktoken_encoders = [];
6 private $encoder_provider = null;
7
8 public function __construct( $core ) {
9 $this->core = $core;
10 }
11
12 /**
13 * Get the cl100k_base tiktoken encoder
14 * Note: We always use cl100k_base regardless of model since it's the standard for all modern OpenAI models
15 * @return object|null The tiktoken encoder or null if not available
16 */
17 private function get_tiktoken_encoder() {
18 // Return cached encoder if available
19 if ( isset( $this->tiktoken_encoders['cl100k_base'] ) ) {
20 return $this->tiktoken_encoders['cl100k_base'];
21 }
22
23 try {
24 // Check if class exists
25 if ( !class_exists( 'Yethee\Tiktoken\EncoderProvider' ) ) {
26 error_log( '[AI Engine Tiktoken] EncoderProvider class not found. Check if composer autoload is working.' );
27 return null;
28 }
29
30 // Initialize encoder provider if needed
31 if ( $this->encoder_provider === null ) {
32 $this->encoder_provider = new \Yethee\Tiktoken\EncoderProvider();
33 }
34
35 // Get the cl100k_base encoder (standard for modern OpenAI models)
36 $encoder = $this->encoder_provider->get( 'cl100k_base' );
37 $this->tiktoken_encoders['cl100k_base'] = $encoder;
38
39 // Removed success log to reduce noise
40 return $encoder;
41 }
42 catch ( \Exception $e ) {
43 error_log( '[AI Engine Tiktoken] Failed to initialize encoder: ' . $e->getMessage() );
44 return null;
45 }
46 }
47
48 public function estimate_tokens( $text = '', $model = null ) {
49 if ( !is_string( $text ) ) {
50 $text = is_array( $text ) || is_object( $text ) ? json_encode( $text ) : (string) $text;
51 }
52
53 // Apply filters for customization
54 $text = apply_filters( 'mwai_estimate_tokens_text', $text, $model );
55 $tokens = apply_filters( 'mwai_estimate_tokens', null, $text, $model );
56 if ( $tokens !== null ) {
57 return $tokens;
58 }
59
60 // Try to use tiktoken for accurate counting (cl100k_base encoder)
61 $encoder = $this->get_tiktoken_encoder();
62 if ( $encoder ) {
63 try {
64 $encoded = $encoder->encode( $text );
65 $token_count = count( $encoded );
66
67 // Comparison logging removed - tiktoken is working correctly
68
69 return $token_count;
70 }
71 catch ( Exception $e ) {
72 error_log( '[AI Engine Tiktoken] Encoding failed, falling back to estimation: ' . $e->getMessage() );
73 }
74 }
75 else {
76 error_log( '[AI Engine Tiktoken] Encoder not available, using fallback' );
77 }
78
79 // Fallback to old estimation method
80 return $this->fallback_estimate_tokens( $text );
81 }
82
83 /**
84 * Fallback token estimation method (the original implementation)
85 */
86 private function fallback_estimate_tokens( $text ) {
87 $multiplier = 4;
88 $hasChineseChars = preg_match( '/[\x{4e00}-\x{9fa5}]/u', $text );
89 $hasJapaneseChars = preg_match( '/[\x{3040}-\x{309f}\x{30a0}-\x{30ff}]/u', $text );
90 $hasKoreanChars = preg_match( '/[\x{ac00}-\x{d7af}]/u', $text );
91 if ( $hasChineseChars || $hasJapaneseChars || $hasKoreanChars ) {
92 $multiplier = 2;
93 }
94 $tokens = (int) ( ( function_exists( 'mb_strlen' ) ? mb_strlen( $text ) : strlen( $text ) ) / $multiplier );
95 return $tokens;
96 }
97
98 public function record_tokens_usage( $model, $in_tokens, $out_tokens = 0, $returned_price = null ) {
99 if ( !is_numeric( $in_tokens ) ) {
100 $in_tokens = 0;
101 }
102 if ( !is_numeric( $out_tokens ) ) {
103 $out_tokens = 0;
104 }
105
106 // Normalize returned_price - keep null when price is unavailable
107 $price_for_tracking = 0; // For monthly/daily accumulation
108 if ( !empty( $returned_price ) || $returned_price === 0 ) {
109 $returned_price = is_array( $returned_price ) ?
110 ( isset( $returned_price['price'] ) ? $returned_price['price'] : null ) :
111 ( is_numeric( $returned_price ) ? $returned_price : null );
112 $price_for_tracking = $returned_price ?? 0;
113 }
114 else {
115 $returned_price = null;
116 }
117
118 // Record monthly usage
119 $usage = $this->core->get_option( 'ai_usage' );
120 $month = date( 'Y-m' );
121 if ( !isset( $usage[$month] ) ) {
122 $usage[$month] = [];
123 }
124 if ( !isset( $usage[$month][$model] ) ) {
125 $usage[$month][$model] = [
126 'prompt_tokens' => 0,
127 'completion_tokens' => 0,
128 'total_tokens' => 0,
129 'returned_price' => 0,
130 'queries' => 0
131 ];
132 }
133 // Ensure all token fields exist for existing data
134 if ( !isset( $usage[$month][$model]['prompt_tokens'] ) ) {
135 $usage[$month][$model]['prompt_tokens'] = 0;
136 }
137 if ( !isset( $usage[$month][$model]['completion_tokens'] ) ) {
138 $usage[$month][$model]['completion_tokens'] = 0;
139 }
140 if ( !isset( $usage[$month][$model]['total_tokens'] ) ) {
141 $usage[$month][$model]['total_tokens'] = 0;
142 }
143 if ( !isset( $usage[$month][$model]['returned_price'] ) ) {
144 $usage[$month][$model]['returned_price'] = 0;
145 }
146 if ( !isset( $usage[$month][$model]['queries'] ) ) {
147 $usage[$month][$model]['queries'] = 0;
148 }
149 $usage[$month][$model]['prompt_tokens'] += $in_tokens;
150 $usage[$month][$model]['completion_tokens'] += $out_tokens;
151 $usage[$month][$model]['total_tokens'] += $in_tokens + $out_tokens;
152 $usage[$month][$model]['queries'] += 1;
153 $usage[$month][$model]['returned_price'] += $price_for_tracking;
154
155 // Clean up old monthly data (keep only last 2 years)
156 $this->cleanup_old_monthly_data( $usage );
157 $this->core->update_option( 'ai_usage', $usage );
158
159 // Record daily usage
160 $daily_usage = $this->core->get_option( 'ai_usage_daily', [] );
161 $day = date( 'Y-m-d' );
162 if ( !isset( $daily_usage[$day] ) ) {
163 $daily_usage[$day] = [];
164 }
165 if ( !isset( $daily_usage[$day][$model] ) ) {
166 $daily_usage[$day][$model] = [
167 'prompt_tokens' => 0,
168 'completion_tokens' => 0,
169 'total_tokens' => 0,
170 'returned_price' => 0,
171 'queries' => 0
172 ];
173 }
174 // Ensure all token fields exist for existing data
175 if ( !isset( $daily_usage[$day][$model]['prompt_tokens'] ) ) {
176 $daily_usage[$day][$model]['prompt_tokens'] = 0;
177 }
178 if ( !isset( $daily_usage[$day][$model]['completion_tokens'] ) ) {
179 $daily_usage[$day][$model]['completion_tokens'] = 0;
180 }
181 if ( !isset( $daily_usage[$day][$model]['total_tokens'] ) ) {
182 $daily_usage[$day][$model]['total_tokens'] = 0;
183 }
184 if ( !isset( $daily_usage[$day][$model]['returned_price'] ) ) {
185 $daily_usage[$day][$model]['returned_price'] = 0;
186 }
187 if ( !isset( $daily_usage[$day][$model]['queries'] ) ) {
188 $daily_usage[$day][$model]['queries'] = 0;
189 }
190 $daily_usage[$day][$model]['prompt_tokens'] += $in_tokens;
191 $daily_usage[$day][$model]['completion_tokens'] += $out_tokens;
192 $daily_usage[$day][$model]['total_tokens'] += $in_tokens + $out_tokens;
193 $daily_usage[$day][$model]['queries'] += 1;
194 $daily_usage[$day][$model]['returned_price'] += $price_for_tracking;
195
196 // Clean up old daily data (keep only last 30 days)
197 $this->cleanup_old_daily_data( $daily_usage );
198 $this->core->update_option( 'ai_usage_daily', $daily_usage );
199
200 // Return the usage data for this specific request
201 return [
202 'prompt_tokens' => $in_tokens,
203 'completion_tokens' => $out_tokens,
204 'total_tokens' => $in_tokens + $out_tokens,
205 'price' => $returned_price,
206 'queries' => 1
207 ];
208 }
209
210 public function record_audio_usage( $model, $seconds ) {
211 // Record monthly usage
212 $usage = $this->core->get_option( 'ai_usage' );
213 $month = date( 'Y-m' );
214 if ( !isset( $usage[$month] ) ) {
215 $usage[$month] = [];
216 }
217 if ( !isset( $usage[$month][$model] ) ) {
218 $usage[$month][$model] = [ 'seconds' => 0, 'queries' => 0 ];
219 }
220 if ( !isset( $usage[$month][$model]['seconds'] ) ) {
221 $usage[$month][$model]['seconds'] = 0;
222 }
223 if ( !isset( $usage[$month][$model]['queries'] ) ) {
224 $usage[$month][$model]['queries'] = 0;
225 }
226 $usage[$month][$model]['seconds'] += $seconds;
227 $usage[$month][$model]['queries'] += 1;
228 $this->cleanup_old_monthly_data( $usage );
229 $this->core->update_option( 'ai_usage', $usage );
230
231 // Record daily usage
232 $daily_usage = $this->core->get_option( 'ai_usage_daily', [] );
233 $day = date( 'Y-m-d' );
234 if ( !isset( $daily_usage[$day] ) ) {
235 $daily_usage[$day] = [];
236 }
237 if ( !isset( $daily_usage[$day][$model] ) ) {
238 $daily_usage[$day][$model] = [ 'seconds' => 0, 'queries' => 0 ];
239 }
240 if ( !isset( $daily_usage[$day][$model]['seconds'] ) ) {
241 $daily_usage[$day][$model]['seconds'] = 0;
242 }
243 if ( !isset( $daily_usage[$day][$model]['queries'] ) ) {
244 $daily_usage[$day][$model]['queries'] = 0;
245 }
246 $daily_usage[$day][$model]['seconds'] += $seconds;
247 $daily_usage[$day][$model]['queries'] += 1;
248 $this->cleanup_old_daily_data( $daily_usage );
249 $this->core->update_option( 'ai_usage_daily', $daily_usage );
250
251 // Return the usage data for this specific request
252 return [
253 'seconds' => $seconds,
254 'queries' => 1
255 ];
256 }
257
258 public function record_images_usage( $model, $resolution, $images ) {
259 // Record monthly usage
260 $usage = $this->core->get_option( 'ai_usage' );
261 $month = date( 'Y-m' );
262 if ( !isset( $usage[$month] ) ) {
263 $usage[$month] = [];
264 }
265 if ( !isset( $usage[$month][$model] ) ) {
266 $usage[$month][$model] = [ 'resolution' => [], 'images' => 0, 'queries' => 0 ];
267 }
268 if ( !isset( $usage[$month][$model]['images'] ) ) {
269 $usage[$month][$model]['images'] = 0;
270 }
271 if ( !isset( $usage[$month][$model]['resolution'] ) ) {
272 $usage[$month][$model]['resolution'] = [];
273 }
274 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
275 $usage[$month][$model]['resolution'][$resolution] = 0;
276 }
277 if ( !isset( $usage[$month][$model]['queries'] ) ) {
278 $usage[$month][$model]['queries'] = 0;
279 }
280 $usage[$month][$model]['images'] += $images;
281 $usage[$month][$model]['resolution'][$resolution] += $images;
282 $usage[$month][$model]['queries'] += 1;
283 $this->cleanup_old_monthly_data( $usage );
284 $this->core->update_option( 'ai_usage', $usage );
285
286 // Record daily usage
287 $daily_usage = $this->core->get_option( 'ai_usage_daily', [] );
288 $day = date( 'Y-m-d' );
289 if ( !isset( $daily_usage[$day] ) ) {
290 $daily_usage[$day] = [];
291 }
292 if ( !isset( $daily_usage[$day][$model] ) ) {
293 $daily_usage[$day][$model] = [ 'resolution' => [], 'images' => 0, 'queries' => 0 ];
294 }
295 if ( !isset( $daily_usage[$day][$model]['images'] ) ) {
296 $daily_usage[$day][$model]['images'] = 0;
297 }
298 if ( !isset( $daily_usage[$day][$model]['resolution'] ) ) {
299 $daily_usage[$day][$model]['resolution'] = [];
300 }
301 if ( !isset( $daily_usage[$day][$model]['resolution'][$resolution] ) ) {
302 $daily_usage[$day][$model]['resolution'][$resolution] = 0;
303 }
304 if ( !isset( $daily_usage[$day][$model]['queries'] ) ) {
305 $daily_usage[$day][$model]['queries'] = 0;
306 }
307 $daily_usage[$day][$model]['images'] += $images;
308 $daily_usage[$day][$model]['resolution'][$resolution] += $images;
309 $daily_usage[$day][$model]['queries'] += 1;
310 $this->cleanup_old_daily_data( $daily_usage );
311 $this->core->update_option( 'ai_usage_daily', $daily_usage );
312
313 // Calculate price based on model and resolution
314 $price = 0;
315 $modelInfo = $this->get_model_info( $model );
316 if ( $modelInfo && isset( $modelInfo['resolutions'] ) ) {
317 foreach ( $modelInfo['resolutions'] as $res ) {
318 if ( $res['name'] === $resolution && isset( $res['price'] ) ) {
319 $price = $res['price'] * $images;
320 break;
321 }
322 }
323 }
324
325 // Return the usage data for this specific request
326 return [
327 'images' => $images,
328 'queries' => 1,
329 'price' => $price,
330 'accuracy' => $price > 0 ? 'price' : 'estimated' // 'price' = calculated from known pricing, not from API
331 ];
332 }
333
334 public function record_videos_usage( $model, $resolution, $seconds ) {
335 // Record monthly usage
336 $usage = $this->core->get_option( 'ai_usage' );
337 $month = date( 'Y-m' );
338 if ( !isset( $usage[$month] ) ) {
339 $usage[$month] = [];
340 }
341 if ( !isset( $usage[$month][$model] ) ) {
342 $usage[$month][$model] = [ 'resolution' => [], 'seconds' => 0, 'queries' => 0 ];
343 }
344 if ( !isset( $usage[$month][$model]['seconds'] ) ) {
345 $usage[$month][$model]['seconds'] = 0;
346 }
347 if ( !isset( $usage[$month][$model]['resolution'] ) ) {
348 $usage[$month][$model]['resolution'] = [];
349 }
350 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
351 $usage[$month][$model]['resolution'][$resolution] = 0;
352 }
353 if ( !isset( $usage[$month][$model]['queries'] ) ) {
354 $usage[$month][$model]['queries'] = 0;
355 }
356 $usage[$month][$model]['seconds'] += $seconds;
357 $usage[$month][$model]['resolution'][$resolution] += $seconds;
358 $usage[$month][$model]['queries'] += 1;
359 $this->cleanup_old_monthly_data( $usage );
360 $this->core->update_option( 'ai_usage', $usage );
361
362 // Record daily usage
363 $daily_usage = $this->core->get_option( 'ai_usage_daily', [] );
364 $day = date( 'Y-m-d' );
365 if ( !isset( $daily_usage[$day] ) ) {
366 $daily_usage[$day] = [];
367 }
368 if ( !isset( $daily_usage[$day][$model] ) ) {
369 $daily_usage[$day][$model] = [ 'resolution' => [], 'seconds' => 0, 'queries' => 0 ];
370 }
371 if ( !isset( $daily_usage[$day][$model]['seconds'] ) ) {
372 $daily_usage[$day][$model]['seconds'] = 0;
373 }
374 if ( !isset( $daily_usage[$day][$model]['resolution'] ) ) {
375 $daily_usage[$day][$model]['resolution'] = [];
376 }
377 if ( !isset( $daily_usage[$day][$model]['resolution'][$resolution] ) ) {
378 $daily_usage[$day][$model]['resolution'][$resolution] = 0;
379 }
380 if ( !isset( $daily_usage[$day][$model]['queries'] ) ) {
381 $daily_usage[$day][$model]['queries'] = 0;
382 }
383 $daily_usage[$day][$model]['seconds'] += $seconds;
384 $daily_usage[$day][$model]['resolution'][$resolution] += $seconds;
385 $daily_usage[$day][$model]['queries'] += 1;
386 $this->cleanup_old_daily_data( $daily_usage );
387 $this->core->update_option( 'ai_usage_daily', $daily_usage );
388
389 // Calculate price based on model, resolution, and seconds
390 $price = 0;
391 $modelInfo = $this->get_model_info( $model );
392 if ( $modelInfo && isset( $modelInfo['resolutions'] ) ) {
393 foreach ( $modelInfo['resolutions'] as $res ) {
394 if ( $res['name'] === $resolution && isset( $res['price'] ) ) {
395 // Price is per second for video models
396 $price = $res['price'] * $seconds;
397 break;
398 }
399 }
400 }
401
402 // Return the usage data for this specific request
403 return [
404 'seconds' => $seconds,
405 'queries' => 1,
406 'price' => $price,
407 'accuracy' => $price > 0 ? 'price' : 'estimated' // 'price' = calculated from known pricing, not from API
408 ];
409 }
410
411 private function get_model_info( $model ) {
412 $engines = $this->core->get_option( 'ai_engines' );
413 if ( !$engines || !is_array( $engines ) ) {
414 return null;
415 }
416
417 foreach ( $engines as $engine ) {
418 if ( isset( $engine['models'] ) && is_array( $engine['models'] ) ) {
419 foreach ( $engine['models'] as $modelInfo ) {
420 if ( isset( $modelInfo['model'] ) && $modelInfo['model'] === $model ) {
421 return $modelInfo;
422 }
423 }
424 }
425 }
426
427 return null;
428 }
429
430 private function cleanup_old_monthly_data( &$usage ) {
431 $two_years_ago = date( 'Y-m', strtotime( '-2 years' ) );
432 foreach ( $usage as $month => $data ) {
433 if ( $month < $two_years_ago ) {
434 unset( $usage[$month] );
435 }
436 }
437 }
438
439 private function cleanup_old_daily_data( &$usage ) {
440 $thirty_days_ago = date( 'Y-m-d', strtotime( '-30 days' ) );
441 foreach ( $usage as $day => $data ) {
442 if ( $day < $thirty_days_ago ) {
443 unset( $usage[$day] );
444 }
445 }
446 }
447 }
448