PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.1
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.1
3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 2.8.17 2.8.18 All 259 releases
wpfunnels / includes / core / AI / AIInit.php

AIInit.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.13.1, at includes/core/AI/AIInit.php

181 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AIInit — entry point and provider factory for the WPFunnels AI module.
4 *
5 * The AI surface is FREE: any provider works with the site owner's own key.
6 * Resolution order for the active provider:
7 *
8 * 1. WPFunnels' own encrypted connection.
9 * 2. Mail Mint's connection, when the admin opted in and Mail Mint exposes it.
10 * The call is delegated — the key itself never crosses the boundary.
11 * 3. Nothing connected → WP_Error the UI can act on.
12 *
13 * @package WPFunnels\AI
14 * @since 3.13.0
15 */
16
17 namespace WPFunnels\AI;
18
19 defined( 'ABSPATH' ) || exit;
20
21 use WPFunnels\AI\Providers\AnthropicProvider;
22 use WPFunnels\AI\Providers\DelegatedProvider;
23 use WPFunnels\AI\Providers\GeminiProvider;
24 use WPFunnels\AI\Providers\OpenAIProvider;
25 use WPFunnels\AI\Providers\ProviderInterface;
26 use WPFunnels\AI\Providers\WordPressAIProvider;
27 use WPFunnels\AI\Settings\AISettings;
28
29 /**
30 * Class AIInit
31 */
32 class AIInit {
33
34 /**
35 * Boot the module: migrate legacy keys and make sure tables exist.
36 *
37 * Runs on admin_init rather than plugins_loaded so the one-time work never
38 * touches a front-end request.
39 *
40 * @return void
41 */
42 public static function init() {
43 add_action( 'admin_init', [ __CLASS__, 'maybeUpgrade' ] );
44 }
45
46 /**
47 * One-time upgrade steps, both idempotent and cheap after the first run.
48 *
49 * @return void
50 */
51 public static function maybeUpgrade() {
52 AISettings::migrateLegacyKeys();
53 ConversationStore::ensureTables();
54 }
55
56 /**
57 * Build a provider adapter from explicit credentials — used by the
58 * validate-and-save flow before anything is stored.
59 *
60 * For wordpress_ai the API key is ignored.
61 *
62 * @param string $provider Provider slug.
63 * @param string $api_key Plaintext API key.
64 * @param string $model Model id.
65 * @return ProviderInterface|null
66 */
67 public static function makeProvider( $provider, $api_key, $model = '' ) {
68 if ( '' === (string) $model ) {
69 $model = isset( AISettings::DEFAULT_MODELS[ $provider ] ) ? AISettings::DEFAULT_MODELS[ $provider ] : '';
70 }
71
72 switch ( $provider ) {
73 case 'anthropic':
74 return new AnthropicProvider( $api_key, $model );
75 case 'openai':
76 return new OpenAIProvider( $api_key, $model );
77 case 'gemini':
78 return new GeminiProvider( $api_key, $model );
79 case 'wordpress_ai':
80 return new WordPressAIProvider( '', $model );
81 }
82
83 return null;
84 }
85
86 /**
87 * Override provider for testing.
88 *
89 * @var ProviderInterface|null
90 */
91 public static $override_provider = null;
92
93 /**
94 * Set active provider override.
95 *
96 * @param ProviderInterface|null $provider Provider instance.
97 * @return void
98 */
99 public static function setActiveProvider( $provider ) {
100 self::$override_provider = $provider;
101 }
102
103 /**
104 * Build the adapter for the currently active connection.
105 *
106 * @return ProviderInterface|\WP_Error
107 */
108 public static function activeProvider() {
109 if ( null !== self::$override_provider ) {
110 return self::$override_provider;
111 }
112
113 if ( ! AISettings::isEnabled() ) {
114 return new \WP_Error(
115 'ai_disabled',
116 __( 'The AI Assistant is turned off. Enable it under WPFunnels → Settings → AI.', 'wpfnl' )
117 );
118 }
119
120 $provider = AISettings::getActiveProvider();
121
122 if ( '' !== $provider ) {
123 $adapter = self::makeProvider( $provider, AISettings::getApiKey( $provider ), AISettings::getModel( $provider ) );
124 if ( $adapter ) {
125 return $adapter;
126 }
127 return new \WP_Error( 'ai_not_connected', __( 'Unknown AI provider configured.', 'wpfnl' ) );
128 }
129
130 $borrowed = self::mailMintProvider();
131 if ( $borrowed ) {
132 return $borrowed;
133 }
134
135 return new \WP_Error(
136 'ai_not_connected',
137 __( 'No AI provider is connected. Connect one under WPFunnels → Settings → AI.', 'wpfnl' )
138 );
139 }
140
141 /**
142 * Borrow Mail Mint's AI connection when the admin opted in.
143 *
144 * Prefers the supported filter; falls back to Mail Mint's public factory so
145 * the bridge works on versions that predate the filter.
146 *
147 * @return DelegatedProvider|null
148 */
149 public static function mailMintProvider() {
150 if ( ! AISettings::usesMailMintConnection() ) {
151 return null;
152 }
153
154 /**
155 * Filter: a provider adapter shared by Mail Mint.
156 *
157 * @since 3.13.0
158 * @param object|null $provider Adapter exposing chat(), validateKey(), slug().
159 */
160 $shared = apply_filters( 'mail_mint/ai/shared_provider', null );
161
162 if ( ! DelegatedProvider::isCompatible( $shared ) && class_exists( '\Mint\MRM\Internal\AI\AIInit' ) ) {
163 $shared = \Mint\MRM\Internal\AI\AIInit::activeProvider();
164 if ( is_wp_error( $shared ) ) {
165 return null;
166 }
167 }
168
169 return DelegatedProvider::isCompatible( $shared ) ? new DelegatedProvider( $shared, 'mail-mint' ) : null;
170 }
171
172 /**
173 * Whether a usable connection exists — own or borrowed.
174 *
175 * @return bool
176 */
177 public static function isReady() {
178 return ! is_wp_error( self::activeProvider() );
179 }
180 }
181