PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.9.3
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.9.3
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / services / message-builder.php
ai-engine / classes / services Last commit date
image.php 1 year ago message-builder.php 11 months ago model-environment.php 11 months ago response-id-manager.php 1 year ago session.php 11 months ago usage-stats.php 11 months ago
message-builder.php
339 lines
1 <?php
2
3 /**
4 * Service for building and transforming messages for different AI APIs.
5 *
6 * Simplifies the complex message building logic by breaking it down
7 * into smaller, focused methods.
8 */
9 class Meow_MWAI_Services_MessageBuilder {
10 private Meow_MWAI_Core $core;
11
12 public function __construct( Meow_MWAI_Core $core ) {
13 $this->core = $core;
14 }
15
16 /**
17 * Build messages array for Responses API format
18 */
19 public function build_responses_api_messages( Meow_MWAI_Query_Base $query ): array {
20 $messages = [];
21
22 // Handle different query types
23 if ( $query instanceof Meow_MWAI_Query_Feedback ) {
24 $messages = $this->build_feedback_messages( $query );
25 }
26 else {
27 $messages = $this->convert_messages_to_responses_format( $query->messages );
28 }
29
30 // Add user message with attachments if needed
31 if ( !( $query instanceof Meow_MWAI_Query_Feedback ) ) {
32 $messages = $this->add_user_message_with_attachments( $messages, $query );
33 }
34
35 return $messages;
36 }
37
38 /**
39 * Build messages for feedback queries
40 */
41 private function build_feedback_messages( Meow_MWAI_Query_Feedback $query ): array {
42 $messages = [];
43
44 // Convert existing messages
45 $messages = $this->convert_messages_to_responses_format( $query->messages );
46
47 // Process feedback blocks
48 if ( !empty( $query->blocks ) ) {
49 $messages = $this->add_feedback_results( $messages, $query->blocks );
50 }
51
52 return $messages;
53 }
54
55 /**
56 * Convert role-based messages to Responses API format
57 */
58 private function convert_messages_to_responses_format( array $messages ): array {
59 $converted = [];
60
61 foreach ( $messages as $message ) {
62 if ( !isset( $message['role'] ) ) {
63 // Already in Responses API format
64 $converted[] = $message;
65 continue;
66 }
67
68 // Handle assistant messages with tool calls
69 if ( $message['role'] === 'assistant' && isset( $message['tool_calls'] ) ) {
70 $converted = array_merge(
71 $converted,
72 $this->convert_assistant_with_tools( $message )
73 );
74 }
75 else {
76 // Regular messages stay as-is
77 $converted[] = $message;
78 }
79 }
80
81 return $converted;
82 }
83
84 /**
85 * Convert assistant message with tool calls to separate messages
86 */
87 private function convert_assistant_with_tools( array $message ): array {
88 $messages = [];
89
90 // Add assistant text if present
91 if ( !empty( $message['content'] ) ) {
92 $messages[] = [
93 'role' => 'assistant',
94 'content' => $message['content']
95 ];
96 }
97
98 // Convert each tool call to function_call message
99 if ( isset( $message['tool_calls'] ) ) {
100 foreach ( $message['tool_calls'] as $toolCall ) {
101 $functionCall = Meow_MWAI_Data_FunctionCall::from_tool_call( $toolCall, $message );
102 $messages[] = [
103 'type' => 'function_call',
104 'call_id' => $functionCall->id,
105 'name' => $functionCall->name,
106 'arguments' => $functionCall->get_arguments_json()
107 ];
108 }
109 }
110
111 return $messages;
112 }
113
114 /**
115 * Add feedback results to messages
116 */
117 private function add_feedback_results( array $messages, array $blocks ): array {
118 $functionResults = [];
119 $processedCallIds = [];
120
121 foreach ( $blocks as $block ) {
122 if ( !isset( $block['feedbacks'] ) ) {
123 continue;
124 }
125
126 foreach ( $block['feedbacks'] as $feedback ) {
127 $toolId = $feedback['request']['toolId'] ?? null;
128
129 // Skip duplicates
130 if ( !$toolId || in_array( $toolId, $processedCallIds ) ) {
131 continue;
132 }
133
134 // Create function result object
135 $result = Meow_MWAI_Data_FunctionResult::success(
136 $toolId,
137 $feedback['reply']['value'] ?? null
138 );
139
140 $functionResults[] = $result->to_responses_api_format();
141 $processedCallIds[] = $toolId;
142 }
143 }
144
145 // Add function results at the end
146 return array_merge( $messages, $functionResults );
147 }
148
149 /**
150 * Add user message with attachments
151 */
152 private function add_user_message_with_attachments( array $messages, Meow_MWAI_Query_Base $query ): array {
153 if ( !$query->attachedFile ) {
154 // Simple text message
155 $messages[] = [
156 'role' => 'user',
157 'content' => [
158 [
159 'type' => 'input_text',
160 'text' => $query->get_message()
161 ]
162 ]
163 ];
164 }
165 else {
166 // Message with image attachment
167 $content = [
168 [
169 'type' => 'input_text',
170 'text' => $query->get_message()
171 ]
172 ];
173
174 // Add image
175 $imageUrl = $query->image_remote_upload === 'url'
176 ? $query->attachedFile->get_url()
177 : $query->attachedFile->get_inline_base64_url();
178
179 $content[] = [
180 'type' => 'input_image',
181 'image_url' => $imageUrl
182 ];
183
184 $messages[] = [
185 'role' => 'user',
186 'content' => $content
187 ];
188 }
189
190 return $messages;
191 }
192
193 /**
194 * Build feedback-only messages for Responses API with previous_response_id
195 */
196 public function build_feedback_only_messages( Meow_MWAI_Query_Feedback $query ): array {
197 $messages = [];
198
199 if ( empty( $query->blocks ) ) {
200 return $messages;
201 }
202
203 // Debug: Log the blocks structure
204 $queries_debug = $this->core->get_option( 'queries_debug_mode' );
205 if ( $queries_debug ) {
206 error_log( '[AI Engine Queries] Building feedback messages with ' . count( $query->blocks ) . ' blocks' );
207 foreach ( $query->blocks as $idx => $block ) {
208 error_log( '[AI Engine Queries] Block ' . $idx . ' has ' . count( $block['feedbacks'] ?? [] ) . ' feedbacks' );
209 }
210 }
211
212 // For Responses API, we need to process ALL tool calls from the rawMessage
213 // and ensure we return them in the same order with their outputs
214 foreach ( $query->blocks as $block ) {
215 if ( !isset( $block['feedbacks'] ) || empty( $block['feedbacks'] ) ) {
216 continue;
217 }
218
219 // Get the rawMessage from the first feedback (they should all have the same rawMessage)
220 $rawMessage = $block['feedbacks'][0]['request']['rawMessage'] ?? null;
221
222 if ( !$rawMessage || !isset( $rawMessage['tool_calls'] ) ) {
223 if ( $queries_debug ) {
224 error_log( '[AI Engine Queries] WARNING: No tool_calls found in rawMessage' );
225 }
226 continue;
227 }
228
229 // Process ALL tool calls from the rawMessage in order
230 foreach ( $rawMessage['tool_calls'] as $toolCall ) {
231 $callId = $toolCall['id'];
232
233 // First: Add the function_call message
234 $functionCall = Meow_MWAI_Data_FunctionCall::from_tool_call( $toolCall );
235 $messages[] = [
236 'type' => 'function_call',
237 'call_id' => $functionCall->id,
238 'name' => $functionCall->name,
239 'arguments' => $functionCall->get_arguments_json()
240 ];
241
242 if ( $queries_debug ) {
243 error_log( '[AI Engine Queries] Added function_call for: ' . $functionCall->name . ' (call_id: ' . $callId . ')' );
244 }
245
246 // Second: Find and add the corresponding function result
247 $foundResult = false;
248 foreach ( $block['feedbacks'] as $feedback ) {
249 if ( ( $feedback['request']['toolId'] ?? null ) === $callId ) {
250 $result = Meow_MWAI_Data_FunctionResult::success( $callId, $feedback['reply']['value'] ?? '' );
251 $messages[] = $result->to_responses_api_format();
252 $foundResult = true;
253
254 if ( $queries_debug ) {
255 error_log( '[AI Engine Queries] Added function_call_output for call_id: ' . $callId );
256 }
257 break;
258 }
259 }
260
261 if ( !$foundResult ) {
262 // This should not happen, but if we can't find the result, add an error result
263 if ( $queries_debug ) {
264 error_log( '[AI Engine Queries] ERROR: No result found for call_id: ' . $callId );
265 }
266 $result = Meow_MWAI_Data_FunctionResult::failure( $callId, 'Function result not found' );
267 $messages[] = $result->to_responses_api_format();
268 }
269 }
270 }
271
272 // Debug: Log final messages structure
273 if ( $queries_debug ) {
274 error_log( '[AI Engine Queries] Total feedback messages built: ' . count( $messages ) );
275 foreach ( $messages as $idx => $msg ) {
276 error_log( '[AI Engine Queries] Message ' . $idx . ' - type: ' . ( $msg['type'] ?? 'unknown' ) . ', call_id: ' . ( $msg['call_id'] ?? 'none' ) );
277 }
278 }
279
280 return $messages;
281 }
282
283 /**
284 * Build messages for Chat Completions API
285 */
286 public function build_chat_completions_messages( Meow_MWAI_Query_Base $query ): array {
287 $messages = [];
288
289 // Add system message if present
290 if ( !empty( $query->instructions ) ) {
291 $messages[] = [
292 'role' => 'system',
293 'content' => $query->instructions
294 ];
295 }
296
297 // Add conversation messages
298 if ( !empty( $query->messages ) ) {
299 $messages = array_merge( $messages, $query->messages );
300 }
301
302 // Add current user message
303 if ( !( $query instanceof Meow_MWAI_Query_Feedback ) ) {
304 $messages[] = [
305 'role' => 'user',
306 'content' => $query->get_message()
307 ];
308 }
309
310 return $messages;
311 }
312
313 /**
314 * Validate message order for Responses API
315 */
316 public function validate_message_order( array $messages ): bool {
317 // Responses API is flexible with message order
318 // but certain patterns should be maintained
319
320 // Check for function_call followed by function_call_output
321 for ( $i = 0; $i < count( $messages ) - 1; $i++ ) {
322 $current = $messages[$i];
323 $next = $messages[$i + 1];
324
325 // If we have a function_call, the next related message should be function_call_output
326 if ( isset( $current['type'] ) && $current['type'] === 'function_call' ) {
327 if ( isset( $next['type'] ) && $next['type'] === 'function_call_output' ) {
328 // Validate matching call_ids
329 if ( $current['call_id'] !== $next['call_id'] ) {
330 return false;
331 }
332 }
333 }
334 }
335
336 return true;
337 }
338 }
339