PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.4.4
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.4.4
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 7 months ago message-builder.php 8 months ago model-environment.php 7 months ago response-id-manager.php 3 months ago session.php 11 months ago usage-stats.php 6 months ago
message-builder.php
375 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 // Get all attachments using the unified method
154 $attachments = method_exists( $query, 'getAttachments' ) ? $query->getAttachments() : [];
155
156 if ( empty( $attachments ) ) {
157 // Simple text message
158 $messages[] = [
159 'role' => 'user',
160 'content' => [
161 [
162 'type' => 'input_text',
163 'text' => $query->get_message()
164 ]
165 ]
166 ];
167 }
168 else {
169 // Message with file/image attachment(s)
170 $content = [
171 [
172 'type' => 'input_text',
173 'text' => $query->get_message()
174 ]
175 ];
176
177 // Process all attachments
178 foreach ( $attachments as $file ) {
179 // Check file type to determine how to handle it
180 // Images can be loaded via URL or base64, but PDFs use OpenAI file_id references
181 $mimeType = $file->get_mimeType() ?? '';
182 $isImage = strpos( $mimeType, 'image/' ) === 0;
183
184 if ( $isImage ) {
185 $fileUrl = $query->image_remote_upload === 'url'
186 ? $file->get_url()
187 : $file->get_inline_base64_url();
188
189 $content[] = [
190 'type' => 'input_image',
191 'image_url' => $fileUrl
192 ];
193 }
194 else {
195 // For non-images (PDFs, documents), use file_id reference
196 $fileId = $file->get_refId();
197
198 if ( !$fileId ) {
199 // File should have been uploaded by the engine before message building
200 // If we get here, something went wrong - log and skip this file
201 error_log( '[AI Engine] MessageBuilder: File without file_id encountered (upload should happen in engine)' );
202 continue;
203 }
204
205 // File was uploaded to OpenAI, use file_id reference
206 // Responses API format: { type: 'input_file', file_id: 'file-xxx' }
207 $content[] = [
208 'type' => 'input_file',
209 'file_id' => $fileId
210 ];
211 }
212 }
213
214 $messages[] = [
215 'role' => 'user',
216 'content' => $content
217 ];
218 }
219
220 return $messages;
221 }
222
223 /**
224 * Build feedback-only messages for Responses API with previous_response_id
225 */
226 public function build_feedback_only_messages( Meow_MWAI_Query_Feedback $query ): array {
227 $messages = [];
228
229 if ( empty( $query->blocks ) ) {
230 return $messages;
231 }
232
233
234 // For Responses API with previous_response_id, we should ONLY send function_call_output messages.
235 // The API already knows about the function_call messages from the previous response.
236 // According to OpenAI documentation, we should NOT echo back the function_call messages.
237
238 foreach ( $query->blocks as $block ) {
239 if ( !isset( $block['feedbacks'] ) || empty( $block['feedbacks'] ) ) {
240 continue;
241 }
242
243 // Get the rawMessage from the first feedback (they should all have the same rawMessage)
244 $rawMessage = $block['feedbacks'][0]['request']['rawMessage'] ?? null;
245
246 if ( !$rawMessage || !isset( $rawMessage['tool_calls'] ) ) {
247 continue;
248 }
249
250 // Process ALL tool calls from the rawMessage in order
251 // But ONLY add the function_call_output messages (not the function_call messages)
252 foreach ( $rawMessage['tool_calls'] as $toolCall ) {
253 $callId = $toolCall['id'];
254
255 // Find and add the corresponding function result
256 // We do NOT add the function_call message when using previous_response_id
257 $foundResult = false;
258 foreach ( $block['feedbacks'] as $feedback ) {
259 if ( ( $feedback['request']['toolId'] ?? null ) === $callId ) {
260 $result = Meow_MWAI_Data_FunctionResult::success( $callId, $feedback['reply']['value'] ?? '' );
261 $messages[] = $result->to_responses_api_format();
262 $foundResult = true;
263 break;
264 }
265 }
266
267 if ( !$foundResult ) {
268 // This should not happen, but if we can't find the result, add an error result
269 $result = Meow_MWAI_Data_FunctionResult::failure( $callId, 'Function result not found' );
270 $messages[] = $result->to_responses_api_format();
271 }
272 }
273 }
274
275 return $messages;
276 }
277
278 /**
279 * Build messages for Chat Completions API
280 */
281 public function build_chat_completions_messages( Meow_MWAI_Query_Base $query ): array {
282 $messages = [];
283
284 // Add system message if present
285 if ( !empty( $query->instructions ) ) {
286 $messages[] = [
287 'role' => 'system',
288 'content' => $query->instructions
289 ];
290 }
291
292 // Add conversation messages
293 if ( !empty( $query->messages ) ) {
294 $messages = array_merge( $messages, $query->messages );
295 }
296
297 // Handle feedback queries - add function results
298 if ( $query instanceof Meow_MWAI_Query_Feedback && !empty( $query->blocks ) ) {
299 foreach ( $query->blocks as $block ) {
300 if ( !isset( $block['feedbacks'] ) ) {
301 continue;
302 }
303
304 foreach ( $block['feedbacks'] as $feedback ) {
305 $messages[] = [
306 'role' => 'tool',
307 'tool_call_id' => $feedback['request']['toolId'],
308 'content' => json_encode( $feedback['reply']['value'] ?? '' )
309 ];
310 }
311 }
312 }
313
314 // Add user message (if not a feedback query)
315 if ( !( $query instanceof Meow_MWAI_Query_Feedback ) ) {
316 $userMessage = $this->build_user_message( $query );
317 if ( $userMessage ) {
318 $messages[] = $userMessage;
319 }
320 }
321
322 return $messages;
323 }
324
325 /**
326 * Build user message for Chat Completions API
327 */
328 private function build_user_message( Meow_MWAI_Query_Base $query ): ?array {
329 $message = $query->get_message();
330 if ( empty( $message ) ) {
331 return null;
332 }
333
334 // Get all attachments using the unified method
335 $attachments = method_exists( $query, 'getAttachments' ) ? $query->getAttachments() : [];
336
337 // Handle image attachments (for Chat Completions API)
338 if ( !empty( $attachments ) ) {
339 $content = [
340 [
341 'type' => 'text',
342 'text' => $message
343 ]
344 ];
345
346 // Add first image attachment (Chat Completions format)
347 foreach ( $attachments as $file ) {
348 if ( $file->get_type() === 'image' ) {
349 $imageUrl = $query->image_remote_upload === 'url'
350 ? $file->get_url()
351 : $file->get_inline_base64_url();
352
353 $content[] = [
354 'type' => 'image_url',
355 'image_url' => [
356 'url' => $imageUrl
357 ]
358 ];
359 break; // Chat Completions typically handles one image
360 }
361 }
362
363 return [
364 'role' => 'user',
365 'content' => $content
366 ];
367 }
368
369 // Simple text message
370 return [
371 'role' => 'user',
372 'content' => $message
373 ];
374 }
375 }