PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.7
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.7
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / ai-copilot / settings.php

settings.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.7, at includes/ai-copilot/settings.php

244 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode — AI Copilot settings + capability helpers.
4 *
5 * The Copilot no longer stores credentials of its own. WordPress 7.0 owns
6 * provider credentials (Settings → Connectors) and model routing
7 * (`wp_ai_client_prompt()`, which injects the configured key automatically).
8 * These helpers therefore only carry the per-user "AI assistant" toggle
9 * (`ai.enabled`) and expose the Core capability signals the shell uses to
10 * decide whether to surface the assistant at all. Provider + model selection
11 * is delegated entirely to the Core AI Client — nothing is persisted here.
12 *
13 * @package WPDesktopMode
14 */
15
16 defined( 'ABSPATH' ) || exit;
17
18 /**
19 * Returns the AI settings block for a given user.
20 *
21 * `enabled` defaults to `false`: the assistant is opt-in, turned on from
22 * OS Settings → Features (and only enable-able once a provider is configured).
23 * Provider + model selection is left entirely to the Core AI Client.
24 *
25 * @since 0.5.0
26 *
27 * @param int $user_id
28 * @return array{ enabled: bool }
29 */
30 function desktop_mode_ai_get_settings( $user_id ) {
31 $os = desktop_mode_get_os_settings( (int) $user_id );
32 $ai = isset( $os['ai'] ) && is_array( $os['ai'] ) ? $os['ai'] : array();
33 return array(
34 'enabled' => isset( $ai['enabled'] ) ? (bool) $ai['enabled'] : false,
35 );
36 }
37
38 /**
39 * Whether the Core AI primitives the Copilot depends on are present.
40 *
41 * The assistant is built on the AI Client (generation), the Connectors API
42 * (credentials) and the Abilities API (tools, adopted in a follow-up). When
43 * any of these is missing — e.g. WordPress < 7.0, or AI disabled site-wide
44 * via `wp_supports_ai()` — the shell hides the assistant entirely.
45 *
46 * @since 0.9.4
47 *
48 * @return bool
49 */
50 function desktop_mode_ai_is_available() {
51 return function_exists( 'wp_ai_client_prompt' )
52 && function_exists( 'wp_get_connectors' )
53 && function_exists( 'wp_register_ability' )
54 && function_exists( 'wp_supports_ai' )
55 && wp_supports_ai();
56 }
57
58 /**
59 * Whether a text-generation provider is configured and usable.
60 *
61 * The baseline capability gate: a no-network, deterministic
62 * `is_supported_for_text_generation()` probe against the AI Client registry
63 * (which Core populates from the configured Connectors). This is what plain
64 * text-generation features — e.g. comment scoring, which only needs structured
65 * text output — gate on. The agentic assistant needs more; see
66 * {@see desktop_mode_ai_assistant_provider_configured()}.
67 *
68 * Credentials are supplied by Core from the configured Connector; no API request
69 * is made.
70 *
71 * @since 0.9.4
72 *
73 * @return bool
74 */
75 function desktop_mode_ai_provider_configured() {
76 if ( ! desktop_mode_ai_is_available() ) {
77 return false;
78 }
79 return (bool) wp_ai_client_prompt( 'test' )->is_supported_for_text_generation();
80 }
81
82 /**
83 * Whether a provider that can actually run the agentic assistant is configured.
84 *
85 * Follows the AI Client feature-detection guidance: rather than probing a bare
86 * builder, we configure it the way the assistant actually calls the client —
87 * with a function declaration — before the (no-network, deterministic)
88 * `is_supported_for_text_generation()` check. `ModelRequirements::fromPromptData()`
89 * turns the attached declaration into a `functionDeclarations` requirement, so
90 * the check passes only when an available model supports text generation *and*
91 * function calling — the two capabilities the tool loop depends on.
92 *
93 * Falls back to the plain text-generation gate when the SDK's FunctionDeclaration
94 * class isn't present (e.g. older WordPress).
95 *
96 * @since 0.9.4
97 *
98 * @return bool
99 */
100 function desktop_mode_ai_assistant_provider_configured() {
101 if ( ! desktop_mode_ai_is_available() ) {
102 return false;
103 }
104
105 $probe = desktop_mode_ai_capability_probe_declaration();
106 if ( ! $probe ) {
107 return desktop_mode_ai_provider_configured();
108 }
109
110 return (bool) wp_ai_client_prompt( 'test' )
111 ->using_function_declarations( $probe )
112 ->is_supported_for_text_generation();
113 }
114
115 /**
116 * Builds a throwaway function declaration used only for capability detection.
117 *
118 * Mirrors the shape of the Copilot's real tool declarations so the AI Client's
119 * support check additionally requires function-calling capability. Returns null
120 * when the SDK class isn't available, letting the caller fall back to a plain
121 * text-generation check.
122 *
123 * @since 0.9.4
124 *
125 * @return object|null A `FunctionDeclaration`, or null.
126 */
127 function desktop_mode_ai_capability_probe_declaration() {
128 $class = '\WordPress\AiClient\Tools\DTO\FunctionDeclaration';
129 if ( ! class_exists( $class ) ) {
130 return null;
131 }
132 try {
133 return new $class(
134 'capability_probe',
135 'Feature-detection probe; never invoked.',
136 null
137 );
138 } catch ( \Throwable $e ) {
139 return null;
140 }
141 }
142
143 /**
144 * Whether the AI assistant is active for a given user.
145 *
146 * This is purely the per-user toggle (default off, opt-in). Availability of the Core
147 * APIs and whether a provider key is set are separate, orthogonal checks
148 * ({@see desktop_mode_ai_is_available()} / {@see desktop_mode_ai_provider_configured()})
149 * so callers can distinguish "user turned it off" from "not set up yet".
150 *
151 * @since 0.5.0
152 *
153 * @param int $user_id
154 * @return bool
155 */
156 function desktop_mode_ai_is_enabled( $user_id ) {
157 $ai = desktop_mode_ai_get_settings( (int) $user_id );
158 return ! empty( $ai['enabled'] );
159 }
160
161 /**
162 * Builds the `aiAssistant` shell-config payload for a user.
163 *
164 * The client uses this to decide whether to surface the Cmd+K assistant and
165 * its admin-bar icon at all (`available`) and the user's own on/off toggle
166 * (`enabled`). Two capability gates are reported separately:
167 *
168 * - `assistantProviderConfigured` — a provider that supports text generation
169 * *and* function calling (what the agentic assistant needs). Gates the Cmd+K
170 * assistant, its admin-bar icon, and the "AI assistant" toggle in Features.
171 * - `providerConfigured` — the baseline text-generation gate. Comment scoring
172 * (which only needs text output) gates on this; the client uses it for the
173 * "Score new comments with AI" mirror.
174 *
175 * Provider + model selection is delegated to the Core AI Client, so there is no
176 * per-user preference to carry here.
177 *
178 * @since 0.9.4
179 *
180 * @param int|null $user_id Defaults to the current user.
181 * @return array{ available: bool, providerConfigured: bool, assistantProviderConfigured: bool, enabled: bool, connectorsUrl: string }
182 */
183 function desktop_mode_ai_assistant_config( $user_id = null ) {
184 $user_id = null === $user_id ? get_current_user_id() : (int) $user_id;
185
186 $connectors_url = admin_url( 'options-connectors.php' );
187
188 if ( ! desktop_mode_ai_is_available() ) {
189 return array(
190 'available' => false,
191 'providerConfigured' => false,
192 'assistantProviderConfigured' => false,
193 'enabled' => false,
194 'connectorsUrl' => $connectors_url,
195 );
196 }
197
198 $ai = desktop_mode_ai_get_settings( $user_id );
199
200 return array(
201 'available' => true,
202 'providerConfigured' => desktop_mode_ai_provider_configured(),
203 'assistantProviderConfigured' => desktop_mode_ai_assistant_provider_configured(),
204 'enabled' => (bool) $ai['enabled'],
205 'connectorsUrl' => $connectors_url,
206 );
207 }
208
209 /**
210 * REST: GET `desktop-mode/v1/ai/status`.
211 *
212 * Returns the current {@see desktop_mode_ai_assistant_config()} so the shell
213 * can re-check provider availability without a page reload — e.g. after the
214 * user configures an AI provider in Settings → Connectors.
215 *
216 * @since 0.9.4
217 */
218 function desktop_mode_register_ai_status_rest_route() {
219 register_rest_route(
220 'desktop-mode/v1',
221 '/ai/status',
222 array(
223 'methods' => WP_REST_Server::READABLE,
224 'callback' => 'desktop_mode_rest_ai_status',
225 'permission_callback' => static function () {
226 return is_user_logged_in() && current_user_can( 'read' );
227 },
228 )
229 );
230 }
231 add_action( 'rest_api_init', 'desktop_mode_register_ai_status_rest_route' );
232
233 /**
234 * REST handler for the AI status probe.
235 *
236 * @since 0.9.4
237 *
238 * @return WP_REST_Response
239 */
240 function desktop_mode_rest_ai_status() {
241 return rest_ensure_response( desktop_mode_ai_assistant_config() );
242 }
243
244