false, 'message' => __('API key is required.', 'ai-blog-automator'), 'elapsed_ms' => 0, 'provider' => 'Groq', ); } $result = self::fetch_models( 'https://api.groq.com/openai/v1/models', array( 'Authorization' => 'Bearer ' . (string) $settings['api_key'], 'Accept' => 'application/json', ) ); $result['elapsed_ms'] = (int) round((microtime(true) - $started_at) * 1000); $result['provider'] = 'Groq'; if (empty($result['success'])) { return $result; } $default_model = Bluteem_ABA_Generator::get_default_groq_model(); $models = isset($result['models']) && is_array($result['models']) ? $result['models'] : array(); if (isset($models[$default_model])) { $result['message'] = sprintf( /* translators: %s: model ID */ __('Groq is reachable. The default model %s is available.', 'ai-blog-automator'), $default_model ); } else { $result['message'] = sprintf( /* translators: 1: model count, 2: model ID */ __('Groq is reachable and listed %1$d models. The default model %2$s was not in that list.', 'ai-blog-automator'), count($models), $default_model ); } return $result; } /** * GET a provider /models endpoint. * * @param string $url Model list URL. * @param array $headers Request headers. * @return array */ public static function fetch_models($url, $headers, $safe = false) { $request = $safe ? 'wp_safe_remote_get' : 'wp_remote_get'; $response = $request( $url, array( 'timeout' => 30, 'headers' => $headers, ) ); if (is_wp_error($response)) { $message = $response->get_error_message(); if (false !== stripos($message, 'cURL error 28') || false !== stripos($message, 'timed out')) { $message .= ' ' . __('This usually means your host cannot reach the provider over outbound HTTPS.', 'ai-blog-automator'); } return array( 'success' => false, 'message' => $message, 'models' => array(), ); } $status_code = (int) wp_remote_retrieve_response_code($response); $response_body = (string) wp_remote_retrieve_body($response); if (200 !== $status_code) { $parsed = Bluteem_ABA_Generator::interpret_provider_http_error('AI', $status_code, $response_body); return array( 'success' => false, 'message' => $parsed['message'], 'error_code' => $parsed['error_code'], 'models' => array(), ); } $models = self::parse_model_list($response_body); if (empty($models)) { return array( 'success' => false, 'message' => __('The provider did not return a model list.', 'ai-blog-automator'), 'models' => array(), ); } return array( 'success' => true, 'message' => '', 'models' => $models, ); } /** * Model id => label pairs from a provider model list. * * @param string $response_body Raw JSON body. * @return array */ public static function parse_model_list($response_body) { $data = json_decode((string) $response_body, true); $items = array(); if (is_array($data) && isset($data['data']) && is_array($data['data'])) { $items = $data['data']; } elseif (is_array($data) && isset($data['models']) && is_array($data['models'])) { $items = $data['models']; } $models = array(); foreach ($items as $item) { if (!is_array($item) || empty($item['id']) || !is_string($item['id'])) { continue; } $id = sanitize_text_field($item['id']); if ('' === $id || isset($models[$id])) { continue; } $label = $id; if (!empty($item['display_name']) && is_string($item['display_name'])) { $label = sanitize_text_field($item['display_name']); } $models[$id] = '' !== $label ? $label : $id; if (count($models) >= 100) { break; } } return $models; } }