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