'assistant' === $t['role'] ? 'model' : 'user', 'parts' => array( array( 'text' => $t['text'] ) ), ); } $id = preg_replace( '#^models/#', '', (string) $model ); return array( 'url' => self::BASE . '/' . rawurlencode( $id ) . ':generateContent', 'headers' => array( 'x-goog-api-key' => $key ), 'body' => array( 'systemInstruction' => array( 'parts' => array( array( 'text' => (string) $instructions ) ) ), 'contents' => $contents, // Thinking counts towards the output limit; leave room so the answer fits. 'generationConfig' => array( 'maxOutputTokens' => (int) $max_tokens + self::THINKING_ROOM ), ), ); } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return array */ protected function parse( array $data ) { $c = isset( $data['candidates'][0] ) && is_array( $data['candidates'][0] ) ? $data['candidates'][0] : array(); $parts = array(); foreach ( isset( $c['content']['parts'] ) && is_array( $c['content']['parts'] ) ? $c['content']['parts'] : array() as $p ) { if ( is_array( $p ) && isset( $p['text'] ) && is_string( $p['text'] ) && empty( $p['thought'] ) ) { $parts[] = (string) $p['text']; } } $u = isset( $data['usageMetadata'] ) && is_array( $data['usageMetadata'] ) ? $data['usageMetadata'] : array(); return array( 'text' => implode( '', $parts ), 'in' => isset( $u['promptTokenCount'] ) ? (int) $u['promptTokenCount'] : 0, 'out' => ( isset( $u['candidatesTokenCount'] ) ? (int) $u['candidatesTokenCount'] : 0 ) + ( isset( $u['thoughtsTokenCount'] ) ? (int) $u['thoughtsTokenCount'] : 0 ), 'cut' => isset( $c['finishReason'] ) && 'MAX_TOKENS' === $c['finishReason'], ); } /** * {@inheritDoc} * * @param int $code HTTP status. * @param array $data Decoded reply. * @return string */ public function classify( $code, array $data ) { $status = isset( $data['error']['status'] ) ? strtoupper( (string) $data['error']['status'] ) : ''; $msg = isset( $data['error']['message'] ) ? strtolower( (string) $data['error']['message'] ) : ''; $reason = strtoupper( (string) wp_json_encode( isset( $data['error']['details'] ) ? $data['error']['details'] : array() ) ); if ( 401 === $code || 'UNAUTHENTICATED' === $status || false !== strpos( $reason, 'API_KEY_INVALID' ) || false !== strpos( $msg, 'api key not valid' ) ) { return 'auth'; } if ( false !== strpos( $msg, 'location is not supported' ) || false !== strpos( $msg, 'user location' ) ) { return 'region'; } if ( 429 === $code || 'RESOURCE_EXHAUSTED' === $status ) { return false !== strpos( $msg, 'billing' ) || false !== strpos( $msg, 'exceeded your current quota' ) ? 'quota' : 'rate_limit'; } if ( false !== strpos( $msg, 'exceeds the maximum number of tokens' ) || false !== strpos( $msg, 'too long' ) ) { return 'too_large'; } if ( 404 === $code || 'NOT_FOUND' === $status ) { return 'model'; } if ( 403 === $code || 'PERMISSION_DENIED' === $status ) { return 'permission'; } if ( $code >= 500 ) { return 'server'; } return 'bad_request'; } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return array */ protected function said( array $data ) { return array( isset( $data['error']['message'] ) ? (string) $data['error']['message'] : '', isset( $data['error']['status'] ) ? (string) $data['error']['status'] : '', ); } /** * {@inheritDoc} * * @param string $key API key. * @return array */ protected function models_request( $key ) { return array( 'url' => self::BASE . '?pageSize=200', 'headers' => array( 'x-goog-api-key' => $key ), ); } /** * {@inheritDoc} * * @param array $data Decoded reply. * @return string[] */ protected function models_parse( array $data ) { $out = array(); foreach ( isset( $data['models'] ) && is_array( $data['models'] ) ? $data['models'] : array() as $m ) { $name = is_array( $m ) && isset( $m['name'] ) && is_string( $m['name'] ) ? preg_replace( '#^models/#', '', $m['name'] ) : ''; $can = is_array( $m ) && isset( $m['supportedGenerationMethods'] ) ? (array) $m['supportedGenerationMethods'] : array(); if ( 0 === strpos( $name, 'gemini' ) && in_array( 'generateContent', $can, true ) && ! preg_match( '/(embedding|image|tts|audio|live)/', $name ) ) { $out[] = $name; } } return $out; } }