PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.8
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.8, at includes/ai-copilot/jobs.php

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