'@cf/google/gemma-4-26b-a4b-it', 'light_model' => '@cf/ibm-granite/granite-4.0-h-micro', 'light_tasks' => TaskContext::DEFAULT_LIGHT_TASKS, 'image_model' => '@cf/black-forest-labs/flux-1-schnell', 'budget_enabled' => 1, 'budget_neurons' => 10000, 'budget_action' => 'cheapest', 'budget_email' => 1, 'cloudflare_only' => 1, 'local_cache_ttl' => 3600, 'gateway_cache_ttl' => 3600, ]; public static function boot(): void { add_action('admin_init', [self::class, 'maybeRedirect']); add_action('admin_post_aipcf_setup_apply', [self::class, 'handleApply']); add_action('admin_post_aipcf_setup_skip', [self::class, 'handleSkip']); } public static function onActivate(): void { if (!get_option(self::DONE_OPTION) && !self::isConfigured()) { set_transient(self::REDIRECT_TRANSIENT, get_current_user_id(), 60); } } public static function maybeRedirect(): void { $uid = get_transient(self::REDIRECT_TRANSIENT); if (!$uid || (int) $uid !== get_current_user_id() || wp_doing_ajax() || is_network_admin()) { return; } delete_transient(self::REDIRECT_TRANSIENT); if (isset($_GET['activate-multi'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } wp_safe_redirect(Wizard::url()); exit; } public static function isConfigured(): bool { return get_account_id() !== '' && get_api_key() !== ''; } public static function defaultsApplied(): bool { $s = get_plugin_settings(); return !empty($s['budget_enabled']) && !empty($s['cloudflare_only']) && (string) $s['preferred_model'] !== ''; } /** * Whether the Overview shows the setup steps: until setup is done or skipped, while the * connection is missing, or when the address asks for them with setup=1. */ public static function panelVisible(): bool { if (!empty($_GET['setup'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return true; } return !get_option(self::DONE_OPTION) || !self::isConfigured(); } /** * The Overview address that brings the setup steps back. */ public static function stepsUrl(): string { return Wizard::url(); } public static function applyDefaults(): array { $s = get_plugin_settings(); foreach (self::DEFAULTS as $k => $v) { $s[$k] = $v; } // Prefer a live catalog check: if Gemma 4 is not on this account, fall back to Llama 4 Scout. $ids = array_column(fetch_available_models(), 'id'); if ($ids !== [] && !in_array($s['preferred_model'], $ids, true)) { $s['preferred_model'] = in_array('@cf/meta/llama-4-scout-17b-16e-instruct', $ids, true) ? '@cf/meta/llama-4-scout-17b-16e-instruct' : (string) $ids[0]; } if ($ids !== [] && !in_array($s['light_model'], $ids, true)) { $s['light_model'] = in_array('@cf/meta/llama-3.2-3b-instruct', $ids, true) ? '@cf/meta/llama-3.2-3b-instruct' : ''; } // Local cache only matters without a gateway. if (Gateway::id() !== '') { $s['local_cache_ttl'] = 0; } update_option(AIPCF_SETTINGS_OPTION, $s); flush_model_caches(); // The connector-approval gate is an explicit admin decision; the button is one. if (!is_connector_approved() && class_exists('\\WordPress\\AI\\Connector_Approval\\Approvals_Store')) { $approvals = get_option('wpai_connector_approvals', []); $approvals = is_array($approvals) ? $approvals : []; $approvals[plugin_basename(\AIPCF_FILE)][CloudflareWorkersAiProvider::ID] = true; update_option('wpai_connector_approvals', $approvals); } update_option(self::DONE_OPTION, time(), false); do_action('aipcf_defaults_applied', $s); return $s; } public static function handleApply(): void { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to do this.', 'aipcf-ai-provider-for-cloudflare'), 403); } check_admin_referer('aipcf_setup_apply'); if (!self::isConfigured()) { set_transient('aipcf_notice_' . get_current_user_id(), ['type' => 'error', 'message' => __('Connect to Cloudflare first.', 'aipcf-ai-provider-for-cloudflare') . ' ' . SettingsPage::credentialsHint(true)], 60); wp_safe_redirect(SettingsPage::tabUrl('overview')); exit; } $s = self::applyDefaults(); set_transient('aipcf_notice_' . get_current_user_id(), ['type' => 'success', 'message' => sprintf( /* translators: 1: default model, 2: light model */ __('Recommended defaults applied: %1$s for writing, %2$s for short tasks, a daily budget of 10,000 neurons, no paid fallbacks, caching on.', 'aipcf-ai-provider-for-cloudflare'), \WordPress\CloudflareWorkersAiProvider\pretty_model_name((string) $s['preferred_model']), $s['light_model'] !== '' ? \WordPress\CloudflareWorkersAiProvider\pretty_model_name((string) $s['light_model']) : __('the default model', 'aipcf-ai-provider-for-cloudflare') )], 60); wp_safe_redirect(SettingsPage::tabUrl('overview')); exit; } public static function handleSkip(): void { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to do this.', 'aipcf-ai-provider-for-cloudflare'), 403); } check_admin_referer('aipcf_setup_skip'); update_option(self::DONE_OPTION, time(), false); if (self::isConfigured()) { set_transient('aipcf_notice_' . get_current_user_id(), ['type' => 'info', 'message' => __('Setup skipped. Choose your models on this tab. Show setup steps, in the About card, brings them back.', 'aipcf-ai-provider-for-cloudflare')], 60); wp_safe_redirect(SettingsPage::tabUrl('models')); exit; } // Without a connection the Overview keeps the setup steps, next to the connection fields. set_transient('aipcf_notice_' . get_current_user_id(), ['type' => 'info', 'message' => __('Setup skipped.', 'aipcf-ai-provider-for-cloudflare') . ' ' . SettingsPage::credentialsHint(true)], 60); wp_safe_redirect(SettingsPage::tabUrl('overview')); exit; } }