$http {url, headers, body} from the adapter. * @param int $timeout Seconds (total, also while streaming). * @param callable|null $on_chunk fn( string $chunk ): bool — false stops the transfer (streaming only). * @param callable|null $on_tick fn(): bool — called whenever bytes arrive; false stops. * @return array{status:int,body:string,aborted:bool} * @throws ProviderError Network errors, timeouts and hosts that are not allowed. */ public function post( string $provider, array $http, int $timeout, ?callable $on_chunk = null, ?callable $on_tick = null ): array { $url = (string) ( $http['url'] ?? '' ); if ( ! self::allowed( $provider, $url ) ) { throw new ProviderError( 'bad_request', 'This AI provider address is not allowed.' ); } $headers = (array) ( $http['headers'] ?? array() ); $chunk = null === $on_chunk ? null : static function ( string $data ) use ( $on_chunk ): bool { return false !== $on_chunk( $data ); }; $stream = new StreamState( null !== $chunk || null !== $on_tick, $chunk, $on_tick, $timeout ); if ( $stream->enabled ) { $headers['Accept-Encoding'] = 'identity'; } $stream->attach(); try { $response = wp_safe_remote_post( $url, array( 'headers' => $headers, 'body' => (string) ( $http['body'] ?? '' ), 'timeout' => $timeout, 'redirection' => 0, 'decompress' => ! $stream->enabled, ) ); } finally { $stream->detach(); } if ( $stream->aborted ) { return array( 'status' => 200, 'body' => $stream->body, 'aborted' => true, ); } if ( $stream->timed_out || is_wp_error( $response ) ) { throw new ProviderError( 'network', 'Could not reach the AI provider (network error or timeout).' ); } $status = (int) wp_remote_retrieve_response_code( $response ); $body = (string) wp_remote_retrieve_body( $response ); $aborted = false; // Not streamed as it arrived (buffered by the host, or answered at once): hand a successful SSE body over once. if ( $chunk && $status < 400 && '' !== $body && ! $stream->delivered && preg_match( '/^\s*(event:|data:)/', $body ) ) { $aborted = false === $chunk( $body ); } return array( 'status' => $status, 'body' => $body, 'aborted' => $aborted, ); } }