PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
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 / jobs.php

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

132 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — AI Copilot WP-Cron jobs.
4 *
5 * Registers the async comment-analysis hook that scores a comment for
6 * spam/harmful content and stores the verdict in comment meta.
7 *
8 * Nothing in the plugin schedules this hook: automatic comment scoring was
9 * removed (`docs/migration-comments-ai-scoring.md`). The callback is kept
10 * because the hook name is a frozen identifier that a site may still have
11 * queued events for, and because an external plugin re-implementing the
12 * feature can schedule it rather than re-deriving the prompt and schema.
13 *
14 * Generation routes through the WordPress AI Client (`wp_ai_client_prompt()`),
15 * which sources credentials from Settings → Connectors — the copilot never
16 * handles an API key.
17 *
18 * Hook name:
19 * desktop_mode_ai_analyze_comment ($comment_id, $user_id)
20 *
21 * @package OpenStation
22 */
23
24 defined( 'ABSPATH' ) || exit;
25
26 // ---------------------------------------------------------------------------
27 // Job: comment
28 // ---------------------------------------------------------------------------
29
30 /**
31 * Analyzes a comment and stores the result in comment meta.
32 *
33 * @param int $comment_id The comment ID.
34 * @param int $user_id The user to attribute the request to.
35 */
36 function openstation_ai_job_analyze_comment( $comment_id, $user_id ) {
37 $comment_id = (int) $comment_id;
38 $user_id = (int) $user_id;
39
40 // No usable text-generation provider is configured in Connectors — nothing
41 // to do. Whoever queued the event may have checked already, but the job runs
42 // async, so re-check to avoid failed requests if the provider was removed.
43 if ( ! openstation_ai_provider_configured() ) {
44 return;
45 }
46
47 $comment = get_comment( $comment_id );
48 if ( ! $comment instanceof WP_Comment ) {
49 return;
50 }
51
52 $result = openstation_ai_analyze_comment_now( $comment, $user_id );
53
54 if ( is_wp_error( $result ) ) {
55 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
56 error_log( '[WP OpenStation AI] Comment ' . $comment_id . ' analysis failed: ' . $result->get_error_message() );
57 return;
58 }
59
60 openstation_ai_save_meta( 'comment', $comment_id, $result );
61
62 /**
63 * Fires after a comment has been successfully analyzed by the AI copilot.
64 *
65 * The `$result` array always contains `topic`, `ai_summary`, `harmful`,
66 * and `spam`. Downstream plugins (e.g. a moderation helper) can act on
67 * `harmful` or `spam` here rather than polling meta.
68 *
69 * @param int $comment_id The comment ID.
70 * @param array $result The structured analysis result.
71 * @param WP_Comment $comment The comment object.
72 */
73 do_action( 'openstation_ai_comment_analyzed', $comment_id, $result, $comment );
74 }
75 add_action( 'desktop_mode_ai_analyze_comment', 'openstation_ai_job_analyze_comment', 10, 2 );
76
77 /**
78 * Runs the structured comment analysis through the AI Client.
79 *
80 * Converts the chat-style prompt from {@see openstation_ai_messages_for_comment()}
81 * into a system instruction + user prompt, requests JSON constrained to the
82 * comment schema, and decodes the result.
83 *
84 * @param WP_Comment $comment The comment to analyze.
85 * @param int $user_id Requesting user id.
86 * @return array|WP_Error Structured `{ topic, ai_summary, harmful, spam }` or an error.
87 */
88 function openstation_ai_analyze_comment_now( WP_Comment $comment, $user_id ) {
89 $messages = openstation_ai_messages_for_comment( $comment );
90 $schema = openstation_ai_schema_comment();
91
92 $system = '';
93 $prompt = '';
94 foreach ( $messages as $message ) {
95 $role = isset( $message['role'] ) ? $message['role'] : '';
96 if ( 'system' === $role ) {
97 $system = (string) $message['content'];
98 } elseif ( 'user' === $role ) {
99 $prompt = (string) $message['content'];
100 }
101 }
102
103 $builder = wp_ai_client_prompt( $prompt );
104 if ( '' !== $system ) {
105 $builder = $builder->using_system_instruction( $system );
106 }
107 // Provider + model are chosen by the Core AI Client unless the
108 // model-config filter says otherwise.
109
110 $builder = $builder->as_json_response( openstation_ai_normalize_response_schema( $schema ) );
111 $builder = openstation_ai_apply_model_config(
112 $builder,
113 array(
114 'user_id' => (int) $user_id,
115 'source' => 'ai-copilot/comment-analysis',
116 'has_schema' => true,
117 )
118 );
119
120 $json = $builder->generate_text();
121 if ( is_wp_error( $json ) ) {
122 return $json;
123 }
124
125 $result = json_decode( (string) $json, true );
126 if ( ! is_array( $result ) ) {
127 return new WP_Error( 'openstation_ai_bad_json', 'The AI response was not valid JSON.' );
128 }
129
130 return $result;
131 }
132