PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.2.7
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.2.7
3.5.8 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 / engines / mistral.php
ai-engine / classes / engines Last commit date
anthropic.php 8 months ago chatml.php 7 months ago core.php 7 months ago factory.php 8 months ago google.php 7 months ago mistral.php 9 months ago open-router.php 7 months ago openai.php 7 months ago perplexity.php 10 months ago replicate.php 7 months ago
mistral.php
534 lines
1 <?php
2
3 class Meow_MWAI_Engines_Mistral extends Meow_MWAI_Engines_ChatML {
4
5 public function __construct( $core, $env ) {
6 parent::__construct( $core, $env );
7 }
8
9 protected function set_environment() {
10 $env = $this->env;
11 $this->apiKey = $env['apikey'] ?? null;
12 }
13
14 protected function get_service_name() {
15 return 'Mistral';
16 }
17
18 public function get_models() {
19 // Return dynamically fetched models only
20 return $this->core->get_engine_models( 'mistral' );
21 }
22
23 public static function get_models_static() {
24 return MWAI_MISTRAL_MODELS;
25 }
26
27 protected function build_url( $query, $endpoint = null ) {
28 $endpoint = apply_filters( 'mwai_mistral_endpoint', 'https://api.mistral.ai/v1', $this->env );
29
30 if ( $query instanceof Meow_MWAI_Query_Text || $query instanceof Meow_MWAI_Query_Feedback ) {
31 return $endpoint . '/chat/completions';
32 }
33 else if ( $query instanceof Meow_MWAI_Query_Embed ) {
34 return $endpoint . '/embeddings';
35 }
36 else {
37 throw new Exception( 'Unsupported query type for Mistral.' );
38 }
39 }
40
41 protected function build_headers( $query ) {
42 if ( $query->apiKey ) {
43 $this->apiKey = $query->apiKey;
44 }
45 if ( empty( $this->apiKey ) ) {
46 throw new Exception( 'No Mistral API Key provided. Please check your settings.' );
47 }
48 return [
49 'Content-Type' => 'application/json',
50 'Authorization' => 'Bearer ' . $this->apiKey,
51 'User-Agent' => 'AI Engine',
52 ];
53 }
54
55 protected function build_messages( $query ) {
56 $messages = parent::build_messages( $query );
57
58 // For feedback queries with tool results, ensure proper format for Mistral
59 if ( $query instanceof Meow_MWAI_Query_Feedback ) {
60 foreach ( $messages as &$message ) {
61 // Mistral expects tool messages to have specific format
62 if ( isset( $message['role'] ) && $message['role'] === 'tool' ) {
63 // Ensure content is never empty
64 if ( empty( $message['content'] ) ) {
65 $message['content'] = json_encode( [ 'result' => 'success' ] );
66 }
67 // Ensure content is a string (Mistral requirement)
68 if ( !is_string( $message['content'] ) ) {
69 $message['content'] = json_encode( $message['content'] );
70 }
71 }
72 }
73 }
74
75 return $messages;
76 }
77
78 protected function build_body( $query, $streamCallback = null, $extra = null ) {
79 // Use parent's build_body for standard ChatML format
80 $body = parent::build_body( $query, $streamCallback, $extra );
81
82 // Mistral uses 'max_tokens' instead of 'max_completion_tokens'
83 if ( isset( $body['max_completion_tokens'] ) ) {
84 $body['max_tokens'] = $body['max_completion_tokens'];
85 unset( $body['max_completion_tokens'] );
86 }
87
88 // TEMPORARILY DISABLED: Function calling for Mistral
89 // Remove tools/functions from the request until feedback loop is properly debugged
90 if ( isset( $body['tools'] ) ) {
91 unset( $body['tools'] );
92 }
93 if ( isset( $body['tool_choice'] ) ) {
94 unset( $body['tool_choice'] );
95 }
96
97 return $body;
98 }
99
100 /**
101 * Generate a human-readable name from model ID
102 * Based on Mistral's official naming conventions
103 */
104 private function generate_human_readable_name( $modelId ) {
105 // Extract version from model ID (e.g., "2508" becomes "25.08")
106 $versionMatch = [];
107 preg_match( '/(\d{4})$/', $modelId, $versionMatch );
108 $version = isset( $versionMatch[1] ) ?
109 substr( $versionMatch[1], 0, 2 ) . '.' . substr( $versionMatch[1], 2 ) : '';
110
111 // Handle special cases for latest versions
112 if ( strpos( $modelId, '-latest' ) !== false ) {
113 $modelId = str_replace( '-latest', '', $modelId );
114 $version = 'Latest';
115 }
116
117 // Build the base name
118 $name = '';
119
120 // Magistral models (reasoning)
121 if ( strpos( $modelId, 'magistral' ) !== false ) {
122 if ( strpos( $modelId, 'medium' ) !== false ) {
123 $name = 'Magistral Medium';
124 } else if ( strpos( $modelId, 'small' ) !== false ) {
125 $name = 'Magistral Small';
126 }
127 // Add version number for Magistral
128 // No version suffix for latest models
129 }
130 // Mistral models
131 else if ( strpos( $modelId, 'mistral' ) !== false ) {
132 if ( strpos( $modelId, 'large' ) !== false ) {
133 $name = 'Mistral Large';
134 } else if ( strpos( $modelId, 'medium' ) !== false ) {
135 $name = 'Mistral Medium';
136 } else if ( strpos( $modelId, 'small' ) !== false ) {
137 $name = 'Mistral Small';
138 } else if ( strpos( $modelId, 'saba' ) !== false ) {
139 $name = 'Mistral Saba';
140 } else if ( strpos( $modelId, 'tiny' ) !== false || strpos( $modelId, 'nemo' ) !== false ) {
141 $name = 'Mistral Nemo';
142 } else if ( strpos( $modelId, 'embed' ) !== false ) {
143 $name = 'Mistral Embed';
144 }
145 }
146 // Pixtral models (vision)
147 else if ( strpos( $modelId, 'pixtral' ) !== false ) {
148 if ( strpos( $modelId, 'large' ) !== false ) {
149 $name = 'Pixtral Large';
150 } else if ( strpos( $modelId, '12b' ) !== false ) {
151 $name = 'Pixtral 12B';
152 }
153 // No (Latest) suffix needed
154 }
155 // Codestral models (code)
156 else if ( strpos( $modelId, 'codestral' ) !== false ) {
157 if ( strpos( $modelId, 'embed' ) !== false ) {
158 $name = 'Codestral Embed';
159 } else {
160 $name = 'Codestral';
161 // No version suffix for Codestral
162 }
163 }
164 // Devstral models (dev tools)
165 else if ( strpos( $modelId, 'devstral' ) !== false ) {
166 if ( strpos( $modelId, 'medium' ) !== false ) {
167 $name = 'Devstral Medium';
168 } else if ( strpos( $modelId, 'small' ) !== false ) {
169 $name = 'Devstral Small';
170 // No version suffix for Devstral
171 }
172 // No (Latest) suffix needed
173 }
174 // Ministral models (edge)
175 else if ( strpos( $modelId, 'ministral' ) !== false ) {
176 if ( strpos( $modelId, '8b' ) !== false ) {
177 $name = 'Ministral 8B';
178 } else if ( strpos( $modelId, '3b' ) !== false ) {
179 $name = 'Ministral 3B';
180 }
181 // No (Latest) suffix needed
182 }
183 // Voxtral models (audio)
184 else if ( strpos( $modelId, 'voxtral' ) !== false ) {
185 if ( strpos( $modelId, 'small' ) !== false ) {
186 $name = 'Voxtral Small';
187 } else if ( strpos( $modelId, 'mini' ) !== false ) {
188 $name = 'Voxtral Mini';
189 }
190 if ( strpos( $modelId, 'transcribe' ) !== false ) {
191 $name .= ' Transcribe';
192 }
193 }
194 // Open models
195 else if ( strpos( $modelId, 'open-' ) === 0 ) {
196 if ( strpos( $modelId, 'mistral-7b' ) !== false ) {
197 $name = 'Mistral 7B (Open)';
198 } else if ( strpos( $modelId, 'mistral-nemo' ) !== false ) {
199 $name = 'Mistral Nemo (Open)';
200 } else if ( strpos( $modelId, 'mixtral-8x7b' ) !== false ) {
201 $name = 'Mixtral 8x7B (Open)';
202 } else if ( strpos( $modelId, 'mixtral-8x22b' ) !== false ) {
203 $name = 'Mixtral 8x22B (Open)';
204 }
205 }
206
207 // Fallback to cleaned model ID if no pattern matches
208 if ( empty( $name ) ) {
209 $name = ucwords( str_replace( ['-', '_'], ' ', $modelId ) );
210 }
211
212 return $name;
213 }
214
215 /**
216 * Retrieve the models from Mistral API
217 * Mistral supports a models endpoint similar to OpenAI
218 */
219 public function retrieve_models() {
220 try {
221 $endpoint = apply_filters( 'mwai_mistral_endpoint', 'https://api.mistral.ai/v1', $this->env );
222 $url = $endpoint . '/models';
223
224 if ( empty( $this->apiKey ) ) {
225 throw new Exception( 'No Mistral API Key provided for model retrieval.' );
226 }
227
228 $options = [
229 'headers' => [
230 'Authorization' => 'Bearer ' . $this->apiKey,
231 'User-Agent' => 'AI Engine'
232 ],
233 'timeout' => 10,
234 'sslverify' => false
235 ];
236
237 $response = wp_remote_get( $url, $options );
238
239 if ( is_wp_error( $response ) ) {
240 throw new Exception( 'AI Engine: ' . $response->get_error_message() );
241 }
242
243 $body = json_decode( $response['body'], true );
244
245 // Debug: Log the complete models response from Mistral
246 // error_log( "AI Engine: Mistral Models Response:\n" . print_r( $body, true ) );
247
248 if ( !isset( $body['data'] ) || !is_array( $body['data'] ) ) {
249 throw new Exception( 'AI Engine: Invalid response for Mistral models list.' );
250 }
251
252 $models = [];
253 $seenModels = []; // Track models we've already added to avoid duplicates
254
255 foreach ( $body['data'] as $model ) {
256 $modelId = $model['id'] ?? '';
257
258 // Generate human-readable name based on model ID
259 $modelName = $this->generate_human_readable_name( $modelId );
260
261 // Skip if we've already seen this model name (to avoid alias duplicates)
262 if ( isset( $seenModels[$modelName] ) ) {
263 continue;
264 }
265
266 // Skip specialized models that shouldn't appear in general chat lists
267 // These are models for specific tasks like moderation, OCR, transcription
268 $skipPatterns = [
269 'moderation', // Moderation models
270 'ocr', // OCR-specific models
271 'transcribe', // Transcription-specific models
272 'mistral-embed', // Legacy embed model (we'll include newer ones)
273 'codestral-embed' // Code-specific embed model
274 ];
275
276 $shouldSkip = false;
277 foreach ( $skipPatterns as $pattern ) {
278 if ( strpos( $modelId, $pattern ) !== false ) {
279 $shouldSkip = true;
280 break;
281 }
282 }
283 if ( $shouldSkip ) {
284 continue;
285 }
286
287 // Skip models that are just aliases (they appear in other model's aliases array)
288 // We'll keep the primary model, not the alias entries
289 $isAlias = false;
290 if ( isset( $model['aliases'] ) && is_array( $model['aliases'] ) && count( $model['aliases'] ) > 0 ) {
291 // If this model ID appears in its own aliases, it's likely an alias entry
292 foreach ( $model['aliases'] as $alias ) {
293 if ( $alias !== $modelId && isset( $seenModels[$alias] ) ) {
294 $isAlias = true;
295 break;
296 }
297 }
298 }
299 if ( $isAlias ) {
300 continue;
301 }
302
303 // Set defaults based on model type
304 $maxCompletionTokens = 32768;
305 $maxContextualTokens = 128000;
306 $features = ['completion'];
307 $tags = ['core', 'chat'];
308
309 // Parse capabilities from the API response
310 $capabilities = $model['capabilities'] ?? [];
311
312 // TEMPORARILY DISABLED: Function calling tags
313 // Not adding 'functions' tag since function calling is disabled for Mistral
314 // if ( in_array( 'function_calling', $capabilities ) ||
315 // ( isset( $model['supports_tool_choice'] ) && $model['supports_tool_choice'] ) ) {
316 // $tags[] = 'functions';
317 // $features[] = 'functions';
318 // }
319
320 // Check for vision capability
321 if ( in_array( 'vision', $capabilities ) ) {
322 $tags[] = 'vision';
323 }
324
325 // Check for embeddings capability
326 // Skip older embedding models in favor of newer ones
327 if ( strpos( $modelId, 'embed' ) !== false ) {
328 // Only include the latest embed models
329 if ( $modelId === 'mistral-embed-2312' || $modelId === 'mistral-embed' ) {
330 continue; // Skip legacy embed models
331 }
332 $features = ['embedding'];
333 $tags = ['core', 'embedding'];
334 }
335
336 // Check for audio capability (voxtral models for chat, not transcription)
337 $capabilities = $model['capabilities'] ?? [];
338 if ( isset( $capabilities['audio'] ) && $capabilities['audio'] &&
339 strpos( $modelId, 'transcribe' ) === false ) {
340 $tags[] = 'audio';
341 }
342
343 // Use max_tokens if available
344 if ( isset( $model['max_tokens'] ) ) {
345 $maxCompletionTokens = (int) $model['max_tokens'];
346 }
347
348 // Use context_length if available
349 if ( isset( $model['max_context_length'] ) ) {
350 $maxContextualTokens = (int) $model['max_context_length'];
351 } else if ( isset( $model['context_window'] ) ) {
352 $maxContextualTokens = (int) $model['context_window'];
353 }
354
355 // Determine pricing based on model (prices per million tokens)
356 $priceIn = 0;
357 $priceOut = 0;
358
359 // Updated Mistral pricing (as of 2025)
360 if ( strpos( $modelId, 'magistral' ) !== false ) {
361 // Magistral reasoning models
362 if ( strpos( $modelId, 'medium' ) !== false ) {
363 $priceIn = 4.00;
364 $priceOut = 12.00;
365 } else {
366 $priceIn = 2.00;
367 $priceOut = 6.00;
368 }
369 } else if ( strpos( $modelId, 'mistral-large' ) !== false || strpos( $modelId, 'pixtral-large' ) !== false ) {
370 $priceIn = 3.00;
371 $priceOut = 9.00;
372 } else if ( strpos( $modelId, 'mistral-medium' ) !== false ) {
373 $priceIn = 2.70;
374 $priceOut = 8.10;
375 } else if ( strpos( $modelId, 'mistral-small' ) !== false ) {
376 $priceIn = 1.00;
377 $priceOut = 3.00;
378 } else if ( strpos( $modelId, 'codestral' ) !== false ) {
379 if ( strpos( $modelId, '2501' ) !== false || strpos( $modelId, '2508' ) !== false ) {
380 $priceIn = 0.30;
381 $priceOut = 0.90;
382 } else {
383 $priceIn = 1.00;
384 $priceOut = 3.00;
385 }
386 } else if ( strpos( $modelId, 'devstral' ) !== false ) {
387 $priceIn = 0.50;
388 $priceOut = 1.50;
389 } else if ( strpos( $modelId, 'ministral' ) !== false ) {
390 $priceIn = 0.10;
391 $priceOut = 0.10;
392 } else if ( strpos( $modelId, 'pixtral-12b' ) !== false ) {
393 $priceIn = 0.15;
394 $priceOut = 0.15;
395 } else if ( strpos( $modelId, 'voxtral' ) !== false ) {
396 $priceIn = 0.50;
397 $priceOut = 1.50;
398 } else if ( strpos( $modelId, 'mistral-saba' ) !== false ) {
399 $priceIn = 0.20;
400 $priceOut = 0.60;
401 } else if ( strpos( $modelId, 'open-mistral' ) !== false || strpos( $modelId, 'mistral-tiny' ) !== false ) {
402 $priceIn = 0.15;
403 $priceOut = 0.15;
404 } else if ( strpos( $modelId, 'open-mixtral-8x7b' ) !== false ) {
405 $priceIn = 0.50;
406 $priceOut = 0.50;
407 } else if ( strpos( $modelId, 'open-mixtral-8x22b' ) !== false ) {
408 $priceIn = 0.90;
409 $priceOut = 0.90;
410 } else if ( strpos( $modelId, 'embed' ) !== false ) {
411 $priceIn = 0.10;
412 $priceOut = 0.00;
413 } else {
414 // Default pricing for unknown models
415 $priceIn = 1.00;
416 $priceOut = 3.00;
417 }
418
419 // Mark this model as seen
420 $seenModels[$modelName] = true;
421
422 // Only include latest models and key open-source versions
423 // This keeps the list clean and manageable
424 $preferredModels = [
425 // Latest versions (primary models)
426 'mistral-large-latest',
427 'mistral-medium-latest',
428 'mistral-small-latest',
429 'mistral-tiny-latest',
430 'mistral-saba-latest',
431 'pixtral-large-latest',
432 'pixtral-12b-latest',
433 'codestral-latest',
434 'devstral-small-latest',
435 'devstral-medium-latest',
436 'magistral-medium-latest',
437 'magistral-small-latest',
438 'voxtral-small-latest',
439 'voxtral-mini-latest',
440 'ministral-3b-latest',
441 'ministral-8b-latest',
442 // Open-source models (always include)
443 'open-mistral-7b',
444 'open-mistral-nemo',
445 'open-mixtral-8x7b',
446 'open-mixtral-8x22b'
447 ];
448
449 // We're focusing on latest versions, so no versioned models
450 $versionedModels = [];
451
452 // Check if this is a model we want to include
453 $includeModel = in_array( $modelId, $preferredModels ) ||
454 in_array( $modelId, $versionedModels ) ||
455 strpos( $modelId, 'embed' ) !== false; // Always include embedding models
456
457 if ( !$includeModel ) {
458 continue;
459 }
460
461 $models[] = [
462 'model' => $modelId,
463 'name' => $modelName,
464 'family' => 'mistral',
465 'features' => $features,
466 'price' => [
467 'in' => $priceIn,
468 'out' => $priceOut,
469 ],
470 'type' => 'token',
471 'unit' => 1 / 1000000,
472 'maxCompletionTokens' => $maxCompletionTokens,
473 'maxContextualTokens' => $maxContextualTokens,
474 'tags' => $tags,
475 ];
476 }
477
478 return $models;
479 }
480 catch ( Exception $e ) {
481 Meow_MWAI_Logging::error( 'Mistral: Failed to retrieve models: ' . $e->getMessage() );
482 // Return empty array on error - models must be fetched from API
483 return [];
484 }
485 }
486
487
488 /**
489 * Connection check for Mistral API
490 * Tests the API key by listing models
491 */
492 public function connection_check() {
493 try {
494 // Use the retrieve_models method to check connection
495 $models = $this->retrieve_models();
496
497 if ( !is_array( $models ) ) {
498 throw new Exception( 'Invalid response format from Mistral' );
499 }
500
501 $modelCount = count( $models );
502 $availableModels = [];
503
504 // Get first 5 models for display
505 $displayModels = array_slice( $models, 0, 5 );
506 foreach ( $displayModels as $model ) {
507 if ( isset( $model['model'] ) ) {
508 $availableModels[] = $model['model'];
509 }
510 }
511
512 return [
513 'success' => true,
514 'service' => 'Mistral',
515 'message' => "Connection successful. Found {$modelCount} models.",
516 'details' => [
517 'endpoint' => 'https://api.mistral.ai/v1/models',
518 'model_count' => $modelCount,
519 'sample_models' => $availableModels
520 ]
521 ];
522 }
523 catch ( Exception $e ) {
524 return [
525 'success' => false,
526 'service' => 'Mistral',
527 'error' => $e->getMessage(),
528 'details' => [
529 'endpoint' => 'https://api.mistral.ai/v1/models'
530 ]
531 ];
532 }
533 }
534 }