PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.2.53
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.2.53
3.5.8 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 / engines / anthropic.php
ai-engine / classes / engines Last commit date
anthropic.php 2 years ago core.php 2 years ago factory.php 2 years ago google.php 2 years ago huggingface.php 2 years ago openai.php 2 years ago openrouter.php 2 years ago
anthropic.php
302 lines
1 <?php
2
3 class Meow_MWAI_Engines_Anthropic extends Meow_MWAI_Engines_OpenAI
4 {
5
6 // Streaming
7 protected $streamInTokens = null;
8 protected $streamOutTokens = null;
9
10 public function __construct( $core, $env )
11 {
12 parent::__construct( $core, $env );
13 }
14
15 protected function set_environment() {
16 $env = $this->env;
17 $this->apiKey = $env['apikey'];
18 }
19
20 protected function build_url( $query, $endpoint = null ) {
21 $endpoint = apply_filters( 'mwai_anthropic_endpoint', 'https://api.anthropic.com/v1', $this->env );
22 if ( $query instanceof Meow_MWAI_Query_Text ) {
23 $url = trailingslashit( $endpoint ) . 'messages';
24 }
25 else {
26 throw new Exception( 'AI Engine: Unsupported query type.' );
27 }
28 return $url;
29 }
30
31 protected function build_headers( $query ) {
32 parent::build_headers( $query );
33 $headers = array(
34 'Content-Type' => 'application/json',
35 'x-api-key' => $this->apiKey,
36 'anthropic-version' => '2023-06-01',
37 'User-Agent' => 'AI Engine',
38 );
39 return $headers;
40 }
41
42 protected function build_messages( $query ) {
43 $messages = [];
44
45 // Then, if any, we need to add the 'messages', they are already formatted.
46 foreach ( $query->messages as $message ) {
47 $messages[] = $message;
48 }
49
50 // If the first message is not a 'user' role, we remove it.
51 if ( !empty( $messages ) && $messages[0]['role'] !== 'user' ) {
52 array_shift( $messages );
53 }
54
55 // Finally, we need to add the message
56 // If there is a file (image), we need to sent the data (not the URL, as it's not supported by Anthropic yet).
57 $fileUrl = $query->get_file_url();
58 if ( !empty( $fileUrl ) ) {
59 $messages[] = [
60 'role' => 'user',
61 'content' => [
62 [
63 "type" => "text",
64 "text" => $query->get_message()
65 ],
66 [
67 "type" => "image",
68 "source" => [
69 "type" => "base64",
70 "media_type" => "image/jpeg",
71 "data" => $query->get_file_data()
72 ]
73 ]
74 ]
75 ];
76 }
77 else {
78 $messages[] = [ 'role' => 'user', 'content' => $query->get_message() ];
79 }
80
81 return $messages;
82 }
83
84 protected function build_body( $query, $streamCallback = null, $extra = null ) {
85 if ( $query instanceof Meow_MWAI_Query_Text ) {
86 $body = array(
87 "model" => $query->model,
88 "max_tokens" => $query->maxTokens,
89 "temperature" => $query->temperature,
90 "stream" => !is_null( $streamCallback ),
91 );
92
93 if ( !empty( $query->stop ) ) {
94 $body['stop'] = $query->stop;
95 }
96
97 // First, we need to add the first message (the instructions).
98 if ( !empty( $query->instructions ) ) {
99 $body['system'] = $query->instructions;
100 }
101
102 // If there is a context, we need to add it.
103 if ( !empty( $query->context ) ) {
104 if ( empty( $body['system'] ) ) {
105 $body['system'] = "";
106 }
107 $body['system'] = empty( $body['system'] ) ? '' : $body['system'] . "\n\n";
108 $body['system'] = $body['system'] . "Context:\n\n" . $query->context;
109 }
110
111 $body['messages'] = $this->build_messages( $query );
112 return $body;
113 }
114 else {
115 throw new Exception( 'AI Engine: Unsupported query type.' );
116 }
117 }
118
119 protected function stream_data_handler( $json ) {
120 $content = null;
121
122 // Get the data
123 if ( isset( $json['type'] ) && $json['type'] === 'message_start' ) {
124 $usage = $json['message']['usage'];
125 $this->streamInTokens = $usage['input_tokens'];
126 $this->inModel = $json['message']['model'];
127 $this->inId = $json['message']['id'];
128 }
129 else if ( isset( $json['type'] ) && ( $json['type'] === 'delta' || $json['type'] === 'content_block_delta' ) ) {
130 $content = $json['delta']['text'];
131 }
132 else if ( isset( $json['type'] ) && $json['type'] === 'message_delta' ) {
133 $usage = $json['usage'];
134 $this->streamOutTokens = $usage['output_tokens'];
135 }
136 else if ( isset( $json['type'] ) && $json['type'] === 'error' ) {
137 $error = $json['error'];
138 $message = $error['message'];
139 throw new Exception( $message );
140 }
141
142 // Avoid some endings
143 $endings = [ "<|im_end|>", "</s>" ];
144 if ( in_array( $content, $endings ) ) {
145 $content = null;
146 }
147
148 return ( $content === '0' || !empty( $content ) ) ? $content : null;
149 }
150
151 public function run_completion_query( $query, $streamCallback = null ) : Meow_MWAI_Reply {
152 if ( !is_null( $streamCallback ) ) {
153 $this->streamCallback = $streamCallback;
154 add_action( 'http_api_curl', [ $this, 'stream_handler' ], 10, 3 );
155 }
156
157 $body = $this->build_body( $query, $streamCallback );
158 $url = $this->build_url( $query );
159 $headers = $this->build_headers( $query );
160 $options = $this->build_options( $headers, $body );
161
162 try {
163 $res = $this->run_query( $url, $options, $streamCallback );
164 $reply = new Meow_MWAI_Reply( $query );
165
166 $returned_id = null;
167 $returned_model = $this->inModel;
168 $returned_choices = [];
169
170 if ( !is_null( $streamCallback ) ) {
171 // Streamed data
172 if ( empty( $this->streamContent ) ) {
173 $json = json_decode( $this->streamBuffer, true );
174 if ( isset( $json['error']['message'] ) ) {
175 throw new Exception( $json['error']['message'] );
176 }
177 }
178 $returned_id = $this->inId;
179 $returned_model = $this->inModel ? $this->inModel : $query->model;
180 $returned_in_tokens = !is_null( $this->streamInTokens ) ? $this->streamInTokens : null;
181 $returned_out_tokens = !is_null( $this->streamOutTokens ) ? $this->streamOutTokens : null;
182 $returned_choices = [
183 [
184 'message' => [
185 'content' => $this->streamContent,
186 //'function_call' => $this->streamFunctionCall
187 ]
188 ]
189 ];
190 }
191 else {
192 // Regular data
193 $data = $res['data'];
194 if ( empty( $data ) ) {
195 throw new Exception( 'No content received (res is null).' );
196 }
197 if ( !$data['model'] ) {
198 error_log( print_r( $data, 1 ) );
199 throw new Exception( 'Invalid response (no model information).' );
200 }
201 $returned_id = $data['id'];
202 $returned_model = $data['model'];
203 $returned_in_tokens = isset( $data['usage']['input_tokens'] ) ? $data['usage']['input_tokens'] : null;
204 $returned_out_tokens = isset( $data['usage']['output_tokens'] ) ? $data['usage']['output_tokens'] : null;
205 // There is only one choice with
206 $returned_choices = [ [
207 'message' => [
208 'content' => $data['content'][0]['text'],
209 //'function_call' => $data['choices'][0]['delta']['function_call']
210 ]
211 ] ];
212 }
213
214 $reply->set_choices( $returned_choices );
215 if ( !empty( $returned_id ) ) {
216 $reply->set_id( $returned_id );
217 }
218
219 // Handle tokens.
220 $this->handle_tokens_usage( $reply, $query, $returned_model, $returned_in_tokens, $returned_out_tokens );
221
222 return $reply;
223 }
224 catch ( Exception $e ) {
225 error_log( $e->getMessage() );
226 $service = $this->get_service_name();
227 $message = "From $service: " . $e->getMessage();
228 throw new Exception( $message );
229 }
230 }
231
232 protected function get_service_name() {
233 return "Anthropic";
234 }
235
236 public function get_models() {
237 return apply_filters( 'mwai_openai_models', MWAI_ANTHROPIC_MODELS );
238 }
239
240 static public function get_models_static() {
241 return MWAI_ANTHROPIC_MODELS;
242 }
243
244 public function handle_tokens_usage( $reply, $query, $returned_model, $returned_in_tokens, $returned_out_tokens ) {
245 $returned_in_tokens = !is_null( $returned_in_tokens ) ? $returned_in_tokens : $reply->get_in_tokens( $query );
246 $returned_out_tokens = !is_null( $returned_out_tokens ) ? $returned_out_tokens : $reply->get_out_tokens();
247
248 // This is how to retrieve the exact number of tokens used with Anthropic.
249 // However, it doesn't work with streaming and it slows the request.
250
251 if ( !empty( $reply->id ) ) {
252 $url = 'https://anthropic.ai/api/v1/generation?id=' . $reply->id;
253 try {
254
255 // This is the CURL way
256 // $ch = curl_init();
257 // curl_setopt( $ch, CURLOPT_URL, $url );
258 // curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
259 // curl_setopt( $ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $this->apiKey ] );
260 // curl_setopt( $ch, CURLOPT_USERAGENT, 'AI Engine' );
261 // $res = curl_exec( $ch );
262 // curl_close( $ch );
263 // $res = json_decode( $res, true );
264
265 // This is the WordPress way
266 // It currently doesn't work with Anthropic (for mysterious reasons)
267 // $res = wp_remote_get( $url, array(
268 // 'headers' => array(
269 // 'Authorization' => 'Bearer ' . $this->apiKey,
270 // 'User-Agent' => 'AI Engine',
271 // 'Accept' => 'application/json',
272 // ),
273 // 'sslverify' => false,
274 // 'user-agent' => 'AI Engine',
275 // 'timeout' => 30,
276 // 'blocking' => false,
277 // ) );
278
279 if ( isset( $res['data'] ) ) {
280 $data = $res['data'];
281 $returned_model = $data['model'];
282 $returned_in_tokens = $data['tokens_prompt'];
283 $returned_out_tokens = $data['tokens_completion'];
284 $price = $res['usage'];
285 $usage = $this->core->record_tokens_usage( $returned_model, $returned_in_tokens, $returned_out_tokens );
286 $reply->set_usage( $usage );
287 return;
288 }
289 }
290 catch ( Exception $e ) {
291 error_log( $e->getMessage() );
292 }
293 }
294
295 $usage = $this->core->record_tokens_usage( $returned_model, $returned_in_tokens, $returned_out_tokens );
296 $reply->set_usage( $usage );
297 }
298
299 public function get_price( Meow_MWAI_Query_Base $query, Meow_MWAI_Reply $reply ) {
300 return parent::get_price( $query, $reply );
301 }
302 }