register(); // Hook into the first request to wrap the transporter. // This ensures the registry is fully initialized before we wrap it. add_action( 'wp_loaded', array( self::class, 'wrap_transporter' ), 1 ); // Also hook early in admin for immediate availability. add_action( 'admin_init', array( self::class, 'wrap_transporter' ), 1 ); self::$initialized = true; } /** * Gets the shared log manager instance. * * Returns null when the AI Request Logging experiment is disabled, since * nothing has initialized the manager in that case. * * @since 1.3.0 * * @return \WordPress\AI\Logging\AI_Request_Log_Manager|null The log manager, or null when logging is inactive. */ public static function get_log_manager(): ?AI_Request_Log_Manager { return self::$log_manager; } /** * Wraps the SDK's HTTP transporter with logging. * * Uses the public setHttpTransporter() API to replace the transporter * with a logging decorator. */ public static function wrap_transporter(): void { static $wrapped = false; if ( $wrapped || ! self::$log_manager ) { return; } try { $registry = AiClient::defaultRegistry(); // Get the current transporter. $current_transporter = $registry->getHttpTransporter(); // Don't wrap if already wrapped. if ( $current_transporter instanceof Logging_Http_Transporter ) { $wrapped = true; return; } // Create a logging wrapper around the existing transporter. $logging_transporter = new Logging_Http_Transporter( $current_transporter, self::$log_manager ); // Replace the transporter with the logging version. $registry->setHttpTransporter( $logging_transporter ); $wrapped = true; } catch ( \Throwable $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch // Silently fail - logging is optional and shouldn't break the site. } } }