mirror of
https://github.com/WenPai-org/wpmind.git
synced 2025-08-06 13:47:16 +08:00
added support for Anthropic and it is recommended
move code to work with AI from rest class to separate class
This commit is contained in:
parent
1eda5f0e15
commit
e67eca2ee0
14 changed files with 797 additions and 342 deletions
|
@ -13,34 +13,6 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
* Class Mind_Rest
|
||||
*/
|
||||
class Mind_Rest extends WP_REST_Controller {
|
||||
/**
|
||||
* Buffer for streaming response.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $buffer = '';
|
||||
|
||||
/**
|
||||
* Last time the buffer was sent.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $last_send_time = 0;
|
||||
|
||||
/**
|
||||
* Buffer threshold.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private const BUFFER_THRESHOLD = 150;
|
||||
|
||||
/**
|
||||
* Minimum send interval.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
private const MIN_SEND_INTERVAL = 0.05;
|
||||
|
||||
/**
|
||||
* Namespace.
|
||||
*
|
||||
|
@ -79,7 +51,7 @@ class Mind_Rest extends WP_REST_Controller {
|
|||
]
|
||||
);
|
||||
|
||||
// Request OpenAI API.
|
||||
// Request AI API.
|
||||
register_rest_route(
|
||||
$namespace,
|
||||
'/request_ai/',
|
||||
|
@ -105,7 +77,7 @@ class Mind_Rest extends WP_REST_Controller {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get permissions for OpenAI api request.
|
||||
* Get permissions for AI API request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -136,257 +108,17 @@ class Mind_Rest extends WP_REST_Controller {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepare messages for request.
|
||||
*
|
||||
* @param string $user_query user query.
|
||||
* @param string $context context.
|
||||
*/
|
||||
public function prepare_messages( $user_query, $context ) {
|
||||
$messages = [];
|
||||
|
||||
$messages[] = [
|
||||
'role' => 'system',
|
||||
'content' => Mind_Prompts::get_system_prompt( $user_query, $context ),
|
||||
];
|
||||
|
||||
// Optional blocks JSON context.
|
||||
if ( $context ) {
|
||||
$messages[] = [
|
||||
'role' => 'user',
|
||||
'content' => '<context>' . $context . '</context>',
|
||||
];
|
||||
}
|
||||
|
||||
// User Query.
|
||||
$messages[] = [
|
||||
'role' => 'user',
|
||||
'content' => '<user_query>' . $user_query . '</user_query>',
|
||||
];
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request OpenAI API.
|
||||
*
|
||||
* @param array $messages messages.
|
||||
*/
|
||||
public function request_open_ai( $messages ) {
|
||||
$settings = get_option( 'mind_settings', array() );
|
||||
$openai_key = $settings['openai_api_key'] ?? '';
|
||||
|
||||
if ( ! $openai_key ) {
|
||||
$this->send_stream_error( 'no_openai_key_found', __( 'Provide OpenAI key in the plugin settings.', 'mind' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
$body = [
|
||||
'model' => 'gpt-4o',
|
||||
'stream' => true,
|
||||
'top_p' => 0.9,
|
||||
'temperature' => 0.7,
|
||||
'messages' => $messages,
|
||||
];
|
||||
|
||||
/* phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_setopt, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_errno, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_close */
|
||||
|
||||
$ch = curl_init( 'https://api.openai.com/v1/chat/completions' );
|
||||
curl_setopt( $ch, CURLOPT_POST, 1 );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_HTTPHEADER,
|
||||
[
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Bearer ' . $openai_key,
|
||||
]
|
||||
);
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, wp_json_encode( $body ) );
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_WRITEFUNCTION,
|
||||
function ( $curl, $data ) {
|
||||
// Response with error message.
|
||||
if ( $data && strpos( $data, "{\n \"error\": {\n \"message\":" ) !== false ) {
|
||||
$error_data = json_decode( $data, true );
|
||||
|
||||
if ( isset( $error_data['error']['message'] ) ) {
|
||||
$this->send_stream_error( 'openai_error', $error_data['error']['message'] );
|
||||
}
|
||||
|
||||
return strlen( $data );
|
||||
}
|
||||
|
||||
$this->process_stream_chunk( $data );
|
||||
|
||||
return strlen( $data );
|
||||
}
|
||||
);
|
||||
|
||||
curl_exec( $ch );
|
||||
|
||||
if ( curl_errno( $ch ) ) {
|
||||
$this->send_stream_error( 'curl_error', curl_error( $ch ) );
|
||||
}
|
||||
|
||||
curl_close( $ch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to OpenAI.
|
||||
* Send request to AI API.
|
||||
*
|
||||
* @param WP_REST_Request $req request object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function request_ai( WP_REST_Request $req ) {
|
||||
// Set headers for streaming.
|
||||
header( 'Content-Type: text/event-stream' );
|
||||
header( 'Cache-Control: no-cache' );
|
||||
header( 'Connection: keep-alive' );
|
||||
header( 'X-Accel-Buffering: no' );
|
||||
|
||||
ob_implicit_flush( true );
|
||||
ob_end_flush();
|
||||
|
||||
$request = $req->get_param( 'request' ) ?? '';
|
||||
$context = $req->get_param( 'context' ) ?? '';
|
||||
|
||||
if ( ! $request ) {
|
||||
$this->send_stream_error( 'no_request', __( 'Provide request to receive AI response.', 'mind' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
$messages = $this->prepare_messages( $request, $context );
|
||||
|
||||
$this->request_open_ai( $messages );
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build base string
|
||||
*
|
||||
* @param string $base_uri - url.
|
||||
* @param string $method - method.
|
||||
* @param array $params - params.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function build_base_string( $base_uri, $method, $params ) {
|
||||
$r = [];
|
||||
ksort( $params );
|
||||
foreach ( $params as $key => $value ) {
|
||||
$r[] = "$key=" . rawurlencode( $value );
|
||||
}
|
||||
return $method . '&' . rawurlencode( $base_uri ) . '&' . rawurlencode( implode( '&', $r ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process streaming chunk from OpenAI
|
||||
*
|
||||
* @param string $chunk - chunk of data.
|
||||
*/
|
||||
private function process_stream_chunk( $chunk ) {
|
||||
$lines = explode( "\n", $chunk );
|
||||
|
||||
foreach ( $lines as $line ) {
|
||||
if ( strlen( trim( $line ) ) === 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( strpos( $line, 'data: ' ) === 0 ) {
|
||||
$json_data = trim( substr( $line, 6 ) );
|
||||
|
||||
if ( '[DONE]' === $json_data ) {
|
||||
if ( ! empty( $this->buffer ) ) {
|
||||
$this->send_buffered_chunk();
|
||||
}
|
||||
$this->send_stream_chunk( [ 'done' => true ] );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = json_decode( $json_data, true );
|
||||
|
||||
if ( isset( $data['choices'][0]['delta']['content'] ) ) {
|
||||
$content = $data['choices'][0]['delta']['content'];
|
||||
|
||||
// Send immediately for JSON markers.
|
||||
if ( strpos( $content, '```json' ) !== false ||
|
||||
strpos( $content, '```' ) !== false ) {
|
||||
if ( ! empty( $this->buffer ) ) {
|
||||
$this->send_buffered_chunk();
|
||||
}
|
||||
$this->send_stream_chunk( [ 'content' => $content ] );
|
||||
$this->last_send_time = microtime( true );
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->buffer .= $content;
|
||||
$current_time = microtime( true );
|
||||
$time_since_last_send = $current_time - $this->last_send_time;
|
||||
|
||||
if ( strlen( $this->buffer ) >= self::BUFFER_THRESHOLD ||
|
||||
$time_since_last_send >= self::MIN_SEND_INTERVAL ||
|
||||
strpos( $this->buffer, "\n" ) !== false ) {
|
||||
$this->send_buffered_chunk();
|
||||
}
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->send_stream_error( 'json_error', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send buffered chunk
|
||||
*/
|
||||
private function send_buffered_chunk() {
|
||||
if ( empty( $this->buffer ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->send_stream_chunk(
|
||||
[
|
||||
'content' => $this->buffer,
|
||||
]
|
||||
);
|
||||
|
||||
$this->buffer = '';
|
||||
$this->last_send_time = microtime( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send stream chunk
|
||||
*
|
||||
* @param array $data - data to send.
|
||||
*/
|
||||
private function send_stream_chunk( $data ) {
|
||||
echo 'data: ' . wp_json_encode( $data ) . "\n\n";
|
||||
|
||||
if ( ob_get_level() > 0 ) {
|
||||
ob_flush();
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send stream error
|
||||
*
|
||||
* @param string $code - error code.
|
||||
* @param string $message - error message.
|
||||
*/
|
||||
private function send_stream_error( $code, $message ) {
|
||||
$this->send_stream_chunk(
|
||||
[
|
||||
'error' => true,
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
]
|
||||
);
|
||||
Mind_AI_API::instance()->request( $request, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue