'system', 'content' => (string) $instructions, ), ); foreach ( self::turns( $messages ) as $t ) { $turns[] = array( 'role' => $t['role'], 'content' => $t['text'], ); } return array( 'url' => self::URL, 'headers' => array( 'Authorization' => 'Bearer ' . $key ), 'body' => array( 'model' => (string) $model, 'messages' => $turns, // Reasoning counts towards the limit; leave room so the answer fits. 'max_tokens' => (int) $max_tokens + self::THINKING_ROOM, 'stream' => false, ), ); } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return array */ protected function parse( array $data ) { $choice = isset( $data['choices'][0] ) && is_array( $data['choices'][0] ) ? $data['choices'][0] : array(); $text = isset( $choice['message']['content'] ) && is_string( $choice['message']['content'] ) ? $choice['message']['content'] : ''; return array( 'text' => $text, 'in' => isset( $data['usage']['prompt_tokens'] ) ? (int) $data['usage']['prompt_tokens'] : 0, 'out' => isset( $data['usage']['completion_tokens'] ) ? (int) $data['usage']['completion_tokens'] : 0, 'cut' => isset( $choice['finish_reason'] ) && 'length' === $choice['finish_reason'], ); } /** * {@inheritDoc} * * @param int $code HTTP status. * @param array $data Decoded reply. * @return string */ public function classify( $code, array $data ) { list( $said, $ec ) = $this->said( $data ); $msg = strtolower( $said ); $ec = strtolower( $ec ); if ( 401 === $code || false !== strpos( $msg, 'incorrect api key' ) || false !== strpos( $msg, 'invalid api key' ) || false !== strpos( $ec, 'api key' ) ) { return 'auth'; } if ( false !== strpos( $msg, 'credit' ) || false !== strpos( $msg, 'spending limit' ) || false !== strpos( $msg, 'billing' ) ) { return 'quota'; } if ( 429 === $code ) { return 'rate_limit'; } if ( false !== strpos( $msg, 'maximum prompt length' ) || false !== strpos( $msg, 'context length' ) || 413 === $code ) { return 'too_large'; } if ( 404 === $code || ( false !== strpos( $msg, 'model' ) && ( false !== strpos( $msg, 'does not exist' ) || false !== strpos( $msg, 'not found' ) ) ) ) { return 'model'; } if ( 403 === $code ) { return 'permission'; } if ( $code >= 500 ) { return 'server'; } return 'bad_request'; } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return array */ protected function said( array $data ) { $err = isset( $data['error'] ) ? $data['error'] : ''; if ( is_array( $err ) ) { return array( isset( $err['message'] ) ? (string) $err['message'] : '', isset( $err['code'] ) ? (string) $err['code'] : ( isset( $err['type'] ) ? (string) $err['type'] : '' ) ); } return array( (string) $err, isset( $data['code'] ) ? (string) $data['code'] : '' ); } /** * {@inheritDoc} * * @param string $key API key. * @return array */ protected function models_request( $key ) { return array( 'url' => self::MODELS_URL, 'headers' => array( 'Authorization' => 'Bearer ' . $key ), ); } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return string[] */ protected function models_parse( array $data ) { $out = array(); foreach ( isset( $data['data'] ) && is_array( $data['data'] ) ? $data['data'] : array() as $m ) { $id = is_array( $m ) && isset( $m['id'] ) && is_string( $m['id'] ) ? $m['id'] : ''; if ( 0 === strpos( $id, 'grok' ) && ! preg_match( '/(image|vision|imagine|video)/', $id ) ) { $out[] = $id; } } return $out; } }