PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.0.7
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.0.7
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 / reply.php
ai-engine / classes Last commit date
data 11 months ago engines 10 months ago exceptions 11 months ago modules 10 months ago query 10 months ago rest 10 months ago services 9 months ago admin.php 10 months ago api.php 10 months ago core.php 10 months ago discussion.php 11 months ago event.php 11 months ago init.php 11 months ago logging.php 11 months ago reply.php 10 months ago rest.php 10 months ago
reply.php
338 lines
1 <?php
2
3 class Meow_MWAI_Reply implements JsonSerializable {
4 public $id = null;
5 public $result = '';
6 public $results = [];
7 public $usage = [
8 'prompt_tokens' => 0,
9 'completion_tokens' => 0,
10 'total_tokens' => 0,
11 'price' => null,
12 ];
13 public $usageAccuracy = 'none'; // 'none', 'estimated', 'tokens', 'price', 'full'
14 public $query = null;
15 public $type = 'text';
16
17 // Code interpreter code (separate from main content)
18 public $contentCode = '';
19
20 // This is when models return a message that needs to be executed (functions, tools, etc)
21 public $needFeedbacks = [];
22 public $needClientActions = [];
23
24 public function __construct( $query = null ) {
25 $this->query = $query;
26 }
27
28 #[\ReturnTypeWillChange]
29 public function jsonSerialize() {
30 $isEmbedding = false;
31 $embeddingsDimensions = null;
32 $embedddingsMessage = null;
33 if ( is_array( $this->results ) && count( $this->results ) > 0 ) {
34 $isEmbedding = is_array( $this->results[0] );
35 if ( $isEmbedding ) {
36 $embeddingsDimensions = count( $this->results[0] );
37 $embedddingsMessage = "A $embeddingsDimensions-dimensional embedding was returned.";
38 }
39 }
40 $data = [
41 'result' => $isEmbedding ? $embedddingsMessage : $this->result,
42 'results' => $isEmbedding ? [] : $this->results,
43 'usage' => $this->usage,
44 'system' => [
45 'class' => get_class( $this ),
46 ]
47 ];
48 if ( !empty( $this->needFeedbacks ) ) {
49 $data['needFeedbacks'] = $this->needFeedbacks;
50 }
51 if ( !empty( $this->needClientActions ) ) {
52 $data['needClientActions'] = $this->needClientActions;
53 }
54 if ( !empty( $this->contentCode ) ) {
55 $data['contentCode'] = $this->contentCode;
56 }
57 return $data;
58 }
59
60 public function set_usage( $usage ) {
61 $this->usage = $usage;
62 }
63
64 public function set_usage_accuracy( $accuracy ) {
65 $this->usageAccuracy = $accuracy;
66 }
67
68 public function set_id( $id ) {
69 $this->id = $id;
70 }
71
72 public function set_type( $type ) {
73 $this->type = $type;
74 }
75
76 public function get_total_tokens() {
77 return isset( $this->usage['total_tokens'] ) ? $this->usage['total_tokens'] : 0;
78 }
79
80 public function get_in_tokens( $query = null ) {
81 $in_tokens = isset( $this->usage['prompt_tokens'] ) ? $this->usage['prompt_tokens'] : 0;
82 if ( empty( $in_tokens ) && $query ) {
83 $in_tokens = $query->get_in_tokens();
84 }
85 return $in_tokens;
86 }
87
88 public function get_out_tokens() {
89 $out_tokens = isset( $this->usage['completion_tokens'] ) ? $this->usage['completion_tokens'] : 0;
90 if ( empty( $out_tokens ) ) {
91 $out_tokens = Meow_MWAI_Core::estimate_tokens( $this->result );
92 }
93 return $out_tokens;
94 }
95
96 public function get_price() {
97 // If it's not set return null, but it can be 0
98 if ( !isset( $this->usage['price'] ) ) {
99 return null;
100 }
101 return $this->usage['price'];
102 }
103
104 public function get_usage_accuracy() {
105 return $this->usageAccuracy;
106 }
107
108 public function get_units() {
109 if ( isset( $this->usage['total_tokens'] ) ) {
110 return $this->usage['total_tokens'];
111 }
112 else if ( isset( $this->usage['images'] ) ) {
113 return $this->usage['images'];
114 }
115 else if ( isset( $this->usage['seconds'] ) ) {
116 return $this->usage['seconds'];
117 }
118 return null;
119 }
120
121 public function get_type() {
122 return $this->type;
123 }
124
125 public function set_reply( $reply ) {
126 $this->result = $reply;
127 $this->results[] = [ $reply ];
128 }
129
130 public function replace( $search, $replace ) {
131 $this->result = str_replace( $search, $replace, $this->result );
132 $this->results = array_map( function ( $result ) use ( $search, $replace ) {
133 return str_replace( $search, $replace, $result );
134 }, $this->results );
135 }
136
137 private function extract_arguments( $funcArgs ) {
138 $finalArgs = [];
139 if ( is_string( $funcArgs ) ) {
140 $arguments = trim( str_replace( "\n", '', $funcArgs ) );
141 if ( substr( $arguments, 0, 1 ) == '{' ) {
142 $arguments = json_decode( $arguments, true );
143 $finalArgs = $arguments;
144 }
145 }
146 else if ( is_array( $funcArgs ) ) {
147 $finalArgs = $funcArgs;
148 }
149 return $finalArgs;
150 }
151
152 /**
153 * Set the choices from OpenAI as the results.
154 * The last (or only) result is set as the result.
155 * @param array $choices ID of the model to use.
156 */
157 public function set_choices( $choices, $rawMessage = null ) {
158 $this->results = [];
159
160 // Initialize feedback arrays at the start to accumulate across all choices
161 // This is important for engines like Google that split multiple function calls
162 // into separate choices
163 $this->needFeedbacks = [];
164 $this->needClientActions = [];
165
166
167 if ( is_array( $choices ) ) {
168 foreach ( $choices as $choice ) {
169
170 // It's chat completion
171 if ( isset( $choice['message'] ) ) {
172
173 // It's text content
174 if ( isset( $choice['message']['content'] ) ) {
175 $content = trim( $choice['message']['content'] );
176 $this->results[] = $content;
177 $this->result = $content;
178 }
179
180 // It's a tool call (OpenAI-style and Anthropic-style)
181 $toolCalls = [];
182 if ( isset( $choice['message']['tool_calls'] ) ) {
183 $tools = $choice['message']['tool_calls'];
184 foreach ( $tools as $tool ) {
185 if ( $tool['type'] === 'function' ) {
186 $toolCall = [
187 'toolId' => $tool['id'],
188 //'mode' => 'interactive',
189 'type' => 'tool_call',
190 'name' => trim( $tool['function']['name'] ),
191 'arguments' => $this->extract_arguments( $tool['function']['arguments'] ),
192 // Represent the original message that triggered the function call
193 'rawMessage' => $rawMessage ? $rawMessage : ( isset( $choice['_rawMessage'] ) ? $choice['_rawMessage'] : $choice['message'] ),
194 ];
195 $toolCalls[] = $toolCall;
196 }
197 }
198 }
199
200 // If it's a function call (Open-AI style; usually for a final execution)
201 if ( isset( $choice['message']['function_call'] ) ) {
202 $content = $choice['message']['function_call'];
203 $name = trim( $content['name'] );
204 $args = $content['arguments'] ?? $content['args'] ?? null;
205 $toolCalls[] = [
206 'toolId' => null,
207 'mode' => 'static',
208 'type' => 'function_call',
209 'name' => $name,
210 'arguments' => $this->extract_arguments( $args ),
211 'rawMessage' => $rawMessage ? $rawMessage : ( isset( $choice['_rawMessage'] ) ? $choice['_rawMessage'] : $choice['message'] ),
212 ];
213 }
214
215 // Deep copy tool calls BEFORE adding function references
216 // This prevents the "Duplicate value for 'tool_call_id'" error
217 // when the same function is called multiple times
218 // Note: We need to preserve the toolId for each tool call
219 if ( !empty( $toolCalls ) ) {
220 $toolCalls = json_decode( json_encode( $toolCalls ), true );
221 }
222
223 // Resolve the original function from the query
224 if ( !empty( $toolCalls ) ) {
225 foreach ( $toolCalls as &$toolCall ) {
226 if ( $toolCall['type'] !== 'function_call' && $toolCall['type'] !== 'tool_call' ) {
227 continue;
228 }
229 foreach ( $this->query->functions as $function ) {
230 if ( $function->name == $toolCall['name'] ) {
231 $toolCall['function'] = $function;
232 break;
233 }
234 }
235 }
236 // IMPORTANT: Unset the reference to avoid PHP's foreach reference bug
237 unset( $toolCall );
238 }
239
240 // Add tool calls to existing arrays instead of resetting them
241 // This is crucial for engines like Google that create multiple choices
242 // for multiple function calls in a single response
243 foreach ( $toolCalls as $tcIdx => $toolCall ) {
244 if ( $toolCall['function']->target !== 'js' ) {
245 $this->needFeedbacks[] = $toolCall;
246 }
247 else if ( $toolCall['function']->target === 'js' ) {
248 $this->needClientActions[] = $toolCall;
249 }
250 }
251 }
252
253 // It's text completion
254 else if ( isset( $choice['text'] ) ) {
255
256 // TODO: Assistants return an array (so actually not really a text completion)
257 // We should probably make this clearer and analyze all the outputs from different endpoints.
258 if ( is_array( $choice['text'] ) ) {
259 $text = trim( $choice['text']['value'] );
260 $this->results[] = $text;
261 $this->result = $text;
262 }
263 else {
264 $text = trim( $choice['text'] );
265 $this->results[] = $text;
266 $this->result = $text;
267 }
268 }
269
270 // It's url/image
271 else if ( isset( $choice['url'] ) ) {
272 // TODO: DALL-E 2 and 3 were using URLs, but now they are using b64_json (gpt-image-1 kind of enforced it)
273 $url = trim( $choice['url'] );
274 $this->results[] = $url;
275 $this->result = $url;
276 }
277 else if ( isset( $choice['b64_json'] ) ) {
278 // In that case we need to create a temporary file in WordPress to store the image, and return the URL for it.
279 global $mwai_core;
280
281 // Check if the query has explicitly disabled local download
282 if ( !empty( $this->query ) && $this->query instanceof Meow_MWAI_Query_Image && $this->query->localDownload === null ) {
283 // Query explicitly doesn't want local download, save as temporary upload
284 $localDownload = 'uploads';
285 $expiry = 1 * HOUR_IN_SECONDS; // 1 hour for temporary images
286 }
287 else {
288 // Use the user's AI-generated image settings (same as DALL-E uses)
289 $localDownload = $mwai_core->get_option( 'image_local_download' );
290 $expiry = (int) $mwai_core->get_option( 'image_expires_download' );
291 }
292
293 // The expiry is already in seconds
294 $ttl = $expiry;
295
296 // Use 'library' or 'uploads' based on user settings
297 $target = ( $localDownload === 'library' ) ? 'library' : 'uploads';
298
299 // Prepare metadata similar to regular image queries
300 $metadata = [];
301 if ( !empty( $this->query ) ) {
302 $metadata['query_envId'] = $this->query->envId ?? null;
303 $metadata['query_session'] = $this->query->session ?? null;
304 $metadata['query_model'] = $this->query->model ?? 'gpt-image-1';
305 }
306
307 $url = $mwai_core->files->save_temp_image_from_b64( $choice['b64_json'], 'generated', $ttl, $target, $metadata );
308 if ( is_wp_error( $url ) ) {
309 return $url;
310 }
311 $this->results[] = $url;
312
313 // For chatbot display, append image markdown to the result
314 if ( !empty( $this->result ) ) {
315 $this->result .= "\n\n";
316 }
317 $this->result .= "![Generated Image]($url)";
318 }
319
320 // It's embedding
321 else if ( isset( $choice['embedding'] ) ) {
322 $content = $choice['embedding'];
323 $this->results[] = $content;
324 $this->result = $content;
325 }
326 }
327 }
328 else {
329 $this->result = $choices;
330 $this->results[] = $choices;
331 }
332 }
333
334 public function toJson() {
335 return json_encode( $this );
336 }
337 }
338