provider = $provider; $this->selected_provider = $selected; $this->actual_provider = $actual; $this->fallback_used = $fallback; $this->warnings = $warnings; } } class WP_Agentic_Writer_Provider_Manager { /** * Get provider instance for specific task type * * @param string $type Task type (chat, clarity, planning, writing, refinement, image). * @return WPAW_Provider_Selection_Result Provider selection result with metadata. */ public static function get_provider_for_task( $type ) { $settings = get_option( 'wp_agentic_writer_settings', array() ); $task_providers = $settings['task_providers'] ?? array(); $allow_openrouter_fallback = ! empty( $settings['allow_openrouter_fallback'] ); // Determine which provider to use for this task $requested_provider = $task_providers[ $type ] ?? 'openrouter'; if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( "WPAW Provider Manager: task={$type}, provider_name={$requested_provider}, task_providers=" . json_encode( $task_providers ) ); } $warnings = array(); $fallback_used = false; $actual_provider = $requested_provider; // Get provider instance with fallback logic $provider = self::get_provider_instance( $requested_provider, $type ); $can_fallback_to_openrouter = ( 'openrouter' === $requested_provider ) || $allow_openrouter_fallback; // If provider not configured or unavailable. if ( ! $provider || ! $provider->is_configured() ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( "Provider '{$requested_provider}' not available for task '{$type}'" ); } // Never silently spend OpenRouter credits when user selected another provider. if ( ! $can_fallback_to_openrouter ) { $warnings[] = "Provider '{$requested_provider}' unavailable. No automatic fallback was applied."; return new WPAW_Provider_Selection_Result( $provider, $requested_provider, $requested_provider, false, $warnings ); } $warnings[] = "Provider '{$requested_provider}' unavailable, fell back to OpenRouter"; $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance(); $actual_provider = 'openrouter'; $fallback_used = true; } // For local backend, verify it's actually reachable before using it if ( 'local_backend' === $requested_provider && ! $fallback_used && method_exists( $provider, 'test_connection' ) ) { $test_result = $provider->test_connection(); if ( is_wp_error( $test_result ) ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( "Local Backend not reachable for task '{$type}'. Error: " . $test_result->get_error_message() ); } if ( $can_fallback_to_openrouter ) { $warnings[] = "Local Backend not reachable, fell back to OpenRouter."; $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance(); $actual_provider = 'openrouter'; $fallback_used = true; } else { $warnings[] = "Local Backend not reachable. No automatic fallback was applied."; } } } return new WPAW_Provider_Selection_Result( $provider, $requested_provider, $actual_provider, $fallback_used, $warnings ); } /** * Get provider instance by name * * @param string $provider_name Provider identifier. * @param string $task_type Task type for validation. * @return WP_Agentic_Writer_AI_Provider_Interface|null Provider instance or null. */ private static function get_provider_instance( $provider_name, $task_type ) { switch ( $provider_name ) { case 'local_backend': if ( ! class_exists( 'WP_Agentic_Writer_Local_Backend_Provider' ) ) { require_once plugin_dir_path( __FILE__ ) . 'class-local-backend-provider.php'; } $provider = new WP_Agentic_Writer_Local_Backend_Provider(); break; case 'codex': if ( ! class_exists( 'WP_Agentic_Writer_Codex_Provider' ) ) { require_once plugin_dir_path( __FILE__ ) . 'class-codex-provider.php'; } $provider = new WP_Agentic_Writer_Codex_Provider(); break; case 'openrouter': default: $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance(); break; } // Validate provider supports this task type if ( $provider && ! $provider->supports_task_type( $task_type ) ) { error_log( "Provider '{$provider_name}' does not support task type '{$task_type}'" ); return null; } return $provider; } /** * Get all available providers with their status * * @return array Array of provider info with name, status, supported tasks. */ public static function get_available_providers() { $providers = array(); // OpenRouter (always available) $openrouter = WP_Agentic_Writer_OpenRouter_Provider::get_instance(); $providers['openrouter'] = array( 'name' => 'OpenRouter', 'configured' => $openrouter->is_configured(), 'supports' => array( 'chat', 'clarity', 'planning', 'writing', 'refinement', 'image' ), 'icon' => '☁️', ); // Local Backend if ( class_exists( 'WP_Agentic_Writer_Local_Backend_Provider' ) ) { $local = new WP_Agentic_Writer_Local_Backend_Provider(); $providers['local_backend'] = array( 'name' => 'Local Backend', 'configured' => $local->is_configured(), 'supports' => array( 'chat', 'clarity', 'planning', 'writing', 'refinement' ), 'icon' => '🏠', ); } // Codex if ( class_exists( 'WP_Agentic_Writer_Codex_Provider' ) ) { $codex = new WP_Agentic_Writer_Codex_Provider(); $providers['codex'] = array( 'name' => 'Codex (OpenAI)', 'configured' => $codex->is_configured(), 'supports' => array( 'chat', 'clarity', 'planning', 'writing', 'refinement' ), 'icon' => '🔗', ); } return $providers; } /** * Test all configured providers * * @return array Results of connection tests. */ public static function test_all_providers() { $results = array(); $providers = self::get_available_providers(); foreach ( $providers as $key => $info ) { if ( ! $info['configured'] ) { $results[ $key ] = array( 'success' => false, 'message' => 'Not configured', ); continue; } $provider = self::get_provider_instance( $key, 'chat' ); if ( $provider ) { $test_result = $provider->test_connection(); $results[ $key ] = is_wp_error( $test_result ) ? array( 'success' => false, 'message' => $test_result->get_error_message(), ) : $test_result; } } return $results; } }