This commit is contained in:
Thommy Bucaille 2023-12-22 01:28:00 +01:00
parent 1ec34aa001
commit b41b6bc26e
1 changed files with 152 additions and 162 deletions

View File

@ -1,4 +1,5 @@
<?php
namespace Controllers;
use Core\App;
@ -13,7 +14,8 @@ use Models\User;
use OpenAI;
use OpenAI\Responses\Chat\CreateResponse;
class ChatController extends BaseController{
class ChatController extends BaseController
{
const MODEL_USED = 'gpt-3.5-turbo-1106';
const MAX_MESSAGES = 100;
@ -97,7 +99,8 @@ class ChatController extends BaseController{
public static $NB_REQUEST = 0;
public function store() {
public function store()
{
// No time limit
set_time_limit(0);
@ -107,7 +110,7 @@ class ChatController extends BaseController{
'message' => 'required|string|min:3|max:4096'
]);
if($errors) {
if ($errors) {
return $this->json([
'success' => false,
'result' => null,
@ -120,8 +123,9 @@ class ChatController extends BaseController{
return $this->handle($message);
}
public function handle($message, $role = "user") {
public function handle($message, $role = "user")
{
try {
$this->start();
@ -130,20 +134,20 @@ class ChatController extends BaseController{
]);
// Save result messages
foreach($result as $r) {
foreach($r->choices as $data) {
foreach ($result as $r) {
foreach ($r->choices as $data) {
$allowed_role = ['user', 'system', 'assistant', 'tool'];
if(!in_array($data->message->role, $allowed_role)) continue;
if (!in_array($data->message->role, $allowed_role)) continue;
if($data->message->role == 'tool') {
if ($data->message->role == 'tool') {
self::$context_messages[] = [
'tool_call_id' => $data->tool_call_id,
'name' => $data->message->name,
'role' => $data->message->role,
'content' => $data->message->content,
];
}else{
} else {
self::$context_messages[] = [
'role' => $data->message->role,
'content' => $data->message->content,
@ -152,22 +156,20 @@ class ChatController extends BaseController{
}
}
if(!Helper::isCLI()) {
if(count(self::$context_messages) > self::MAX_MESSAGES) {
if (!Helper::isCLI()) {
if (count(self::$context_messages) > self::MAX_MESSAGES) {
$this->compressRedundantMessages();
}
// Save messages in cache
Cache::set("chat_messages_" . Auth::user()->id, self::$context_messages);
}
return $this->json([
'success' => true,
'result' => $result,
'history' => self::$context_messages,
], 200);
}
catch(\Exception $e) {
} catch (\Exception $e) {
return $this->json([
'success' => false,
@ -179,7 +181,8 @@ class ChatController extends BaseController{
}
}
private function start() {
private function start()
{
try {
// self::$client = OpenAI::client(config('chat.key'));
self::$client = OpenAI::factory()
@ -188,8 +191,8 @@ class ChatController extends BaseController{
->withHttpHeader('OpenAI-Beta', 'assistants=v1')
->make();
if(!Helper::isCLI()) {
if (!Helper::isCLI()) {
// Get messages in cache if exists
self::$context_messages = Cache::get("chat_messages_" . Auth::user()->id, self::$context_messages);
}
@ -363,8 +366,9 @@ class ChatController extends BaseController{
'type' => 'integer',
'description' => 'The memory id',
],
'required' => ['id'],
]
],
'required' => ['id'],
],
],
// Recupérer la date de dernière activité de thommy
@ -563,36 +567,35 @@ class ChatController extends BaseController{
],
],
];
}
catch(\Throwable $th) {
} catch (\Throwable $th) {
throw $th;
}
}
private function sendMessages($messages, $sleep = false) {
private function sendMessages($messages, $sleep = false)
{
if($sleep && self::$NB_REQUEST >= 3) {
if ($sleep && self::$NB_REQUEST >= 3) {
sleep(60);
self::$NB_REQUEST = 0;
}
try {
foreach($messages as $message) {
foreach ($messages as $message) {
$allowed_role = ['user', 'system', 'assistant', 'tool'];
if(!in_array($message['role'], $allowed_role)) continue;
if (!in_array($message['role'], $allowed_role)) continue;
if($message['role'] == 'tool') {
if ($message['role'] == 'tool') {
self::$context_messages[] = [
'tool_call_id' => $message['tool_call_id'],
'name' => $message['name'],
'role' => $message['role'],
'content' => $message['content'],
];
}else{
} else {
self::$context_messages[] = [
'role' => $message['role'],
'content' => $message['content'],
@ -607,12 +610,11 @@ class ChatController extends BaseController{
'tools' => self::$tools,
'user' => Helper::isCLI() ? "system" : Auth::user()->username,
]);
return $this->handleResponse($result);
}
catch(\Throwable $th) {
} catch (\Throwable $th) {
// If Rate limit reached
if($th->getCode() == 429) {
if ($th->getCode() == 429) {
sleep(60);
self::$NB_REQUEST = 0;
return $this->sendMessages($messages, true);
@ -621,27 +623,28 @@ class ChatController extends BaseController{
}
}
private function handleResponse(CreateResponse $response) {
private function handleResponse(CreateResponse $response)
{
$message = $response->choices[0]->message;
$results = [];
if($message) {
if($message->toolCalls) {
foreach($message->toolCalls as $toolCall) {
$function = array_filter(self::$tools, function($tool) use ($toolCall) {
if ($message) {
if ($message->toolCalls) {
foreach ($message->toolCalls as $toolCall) {
$function = array_filter(self::$tools, function ($tool) use ($toolCall) {
return $tool['function']['name'] == $toolCall->function->name;
});
if(count($function) > 0) {
if (count($function) > 0) {
$function = array_values($function)[0];
$parameters = [];
$args = json_decode(($toolCall->function->arguments), true);
foreach($function['function']['parameters']['properties'] as $name => $property) {
foreach ($function['function']['parameters']['properties'] as $name => $property) {
$parameters[] = $args[$name];
}
$result = call_user_func_array([$this, $function['function']['name']], $parameters);
$results[] = [
'tool_call_id' => $toolCall->id,
@ -649,20 +652,19 @@ class ChatController extends BaseController{
'name' => $function['function']['name'],
'content' => $result ?? 'Sorry, I couldn\'t find the function ' . $function['function']['name'] . '.',
];
}else{
} else {
$results[] = [
'tool_call_id' => $toolCall->id,
'role' => 'tool',
'name' => $toolCall->function->name,
'content' => 'Sorry, I couldn\'t find the function ' . $toolCall->function->name . '.',
];
}
}
}
}
if(count($results) > 0) {
if (count($results) > 0) {
// Prepend $message to $results
$append = [
@ -670,7 +672,7 @@ class ChatController extends BaseController{
'content' => $message->content,
];
if($message->toolCalls) {
if ($message->toolCalls) {
$append['tool_calls'] = $message->toolCalls;
}
@ -686,14 +688,15 @@ class ChatController extends BaseController{
/**
* Nettoyage des messages
*/
private function compressRedundantMessages() {
private function compressRedundantMessages()
{
$compressedMessages = [];
foreach (self::$context_messages as $message) {
// Recherchez si un message similaire existe déjà dans les messages compressés
$lastIndex = count($compressedMessages) - 1;
if ($lastIndex >= 0 && $compressedMessages[$lastIndex]['role'] === $message['role'] && in_array($message['role'], ['user','system'])) {
if ($lastIndex >= 0 && $compressedMessages[$lastIndex]['role'] === $message['role'] && in_array($message['role'], ['user', 'system'])) {
// Si le message actuel a le même rôle que le dernier message compressé,
// ajoutez son contenu au contenu du dernier message
$compressedMessages[$lastIndex]['content'] .= "\n" . $message['content'];
@ -706,35 +709,35 @@ class ChatController extends BaseController{
// Mettez à jour le tableau context_messages avec les messages compressés
self::$context_messages = $compressedMessages;
}
/**
* Tools
*/
private function get_current_weather($location, $unit = 'celsius') {
private function get_current_weather($location, $unit = 'celsius')
{
try {
$url = 'https://api.openweathermap.org/data/2.5/weather?q=' . $location . '&appid=' . config('chat.weather_api_key') . '&units=' . $unit;
$data = json_decode(file_get_contents($url), true);
if($data['cod'] == 200) {
if ($data['cod'] == 200) {
return 'The current weather in ' . $location . ' is ' . $data['weather'][0]['description'] . ' with a temperature of ' . $data['main']['temp'] . '°' . strtoupper(substr($unit, 0, 1)) . '.';
}
else {
} else {
return 'Sorry, I couldn\'t find the weather in ' . $location . '.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t find the weather in ' . $location . '. Error: ' . $e->getMessage();
}
}
private function get_location($location) {
private function get_location($location)
{
$url = 'https://api.openweathermap.org/geo/1.0/direct?q=' . urlencode($location) . '&appid=' . config('chat.weather_api_key') . '&limit=1';
$data = json_decode(file_get_contents($url), true);
$location = $data[0];
if($location) {
if ($location) {
return implode(', ', [
'lat' => $location['lat'],
'lon' => $location['lon'],
@ -742,32 +745,31 @@ class ChatController extends BaseController{
'state' => $location['state'],
'country' => $location['country'],
]);
}
else {
} else {
return 'Sorry, I couldn\'t find the location ' . $location . '.';
}
}
private function list_files() {
private function list_files()
{
// List files and folder recursively
$list_files = function($path) use (&$list_files) {
$list_files = function ($path) use (&$list_files) {
$files = glob($path . '/*');
$result = [];
foreach($files as $file) {
foreach ($files as $file) {
// Get relative path from documents folder
$path = str_replace(App::STORAGE_PATH . '/documents/', '', $file);
if(is_dir($file)) {
if (is_dir($file)) {
$result[] = [
'type' => 'folder',
'name' => basename($file),
'path' => $path,
'children' => $list_files($file),
];
}
else {
} else {
$result[] = [
'type' => 'file',
'name' => basename($file),
@ -784,158 +786,151 @@ class ChatController extends BaseController{
return "Here is the list of files and folders in the documents folder (in JSON):\n" . json_encode($data, JSON_PRETTY_PRINT);
}
private function read_file($file) {
private function read_file($file)
{
try {
$path = App::STORAGE_PATH . '/documents/' . $file;
if(file_exists($path)) {
if (file_exists($path)) {
return file_get_contents($path);
}
else {
} else {
return 'Sorry, I couldn\'t find the file ' . $file . '.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t find the file ' . $file . '. Error: ' . $e->getMessage();
}
}
private function write_file($file, $content) {
private function write_file($file, $content)
{
try {
// Get absolute path
$path = App::STORAGE_PATH . '/documents/' . $file;
// If folder not exists, create it
if(!is_dir(dirname($path))) {
if (!is_dir(dirname($path))) {
mkdir(dirname($path), 0777, true);
}
// Write file
$res = file_put_contents($path, $content);
if($res) {
if ($res) {
return 'The file ' . $file . ' has been successfully written.';
}
else {
} else {
return 'Sorry, I couldn\'t write the file ' . $file . '.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t write the file ' . $file . '. Error: ' . $e->getMessage();
}
}
private function delete_file($file) {
private function delete_file($file)
{
try {
// Get absolute path
$path = App::STORAGE_PATH . '/documents/' . $file;
// Delete file
$res = unlink($path);
if($res) {
if ($res) {
return 'The file ' . $file . ' has been successfully deleted.';
}
else {
} else {
return 'Sorry, I couldn\'t delete the file ' . $file . '.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t delete the file ' . $file . '. Error: ' . $e->getMessage();
}
}
private function add_memory($keywords, $content) {
private function add_memory($keywords, $content)
{
try {
$res = Memory::add($keywords, $content);
if($res) {
if ($res) {
return 'The memory data has been successfully added.';
}
else {
} else {
return 'Sorry, I couldn\'t add the memory data.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t add the memory data. Error: ' . $e->getMessage();
}
}
private function search_memory($keywords) {
private function search_memory($keywords)
{
try {
$res = Memory::search($keywords);
if($res) {
if ($res) {
return 'The memory data has been successfully found. Result: ' . json_encode($res);
}
else {
} else {
return 'Sorry, I couldn\'t find the memory data.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t find the memory data. Error: ' . $e->getMessage();
}
}
private function remove_memory($id) {
private function remove_memory($id)
{
try {
$res = Memory::where('id', $id)->delete();
if($res) {
if ($res) {
return 'The memory data has been successfully removed.';
}
else {
} else {
return 'Sorry, I couldn\'t remove the memory data.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t remove the memory data. Error: ' . $e->getMessage();
}
}
private function get_last_activity() {
private function get_last_activity()
{
$thommy = User::find(1);
return 'The last activity of Thommy was on ' . $thommy->last_online . '.';
}
private function open_url($url) {
private function open_url($url)
{
try {
$url = str_replace(' ', '%20', $url);
$res = file_get_contents($url);
if($res) {
if ($res) {
return 'The URL ' . $url . ' has been successfully opened. Content: ' . $res;
}
else {
} else {
return 'Sorry, I couldn\'t open the URL ' . $url . '.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t open the URL ' . $url . '. Error: ' . $e->getMessage();
}
}
private function send_discord_message($user, $message) {
private function send_discord_message($user, $message)
{
try {
$result = json_decode($this->make_discord_request('users/@me/channels', [
'recipient_id' => $user,
]), true);
if(isset($result['id'])) {
if (isset($result['id'])) {
$result = $this->make_discord_request('channels/' . $result['id'] . '/messages', [
'content' => $message,
]);
return 'Result: ' . $result;
}
else {
} else {
return 'Sorry, I couldn\'t send the message to the user ' . $user . '. Result: ' . json_encode($result);
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t send the message to the user ' . $user . '. Error: ' . $e->getMessage();
}
}
private function make_discord_request($endpoint, $data = [], $method = 'POST') {
private function make_discord_request($endpoint, $data = [], $method = 'POST')
{
try {
$url = 'https://discord.com/api/' . $endpoint;
@ -954,26 +949,21 @@ class ChatController extends BaseController{
CURLOPT_SSL_VERIFYPEER => 0
));
if($method == 'POST') {
if ($method == 'POST') {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
else if($method == 'GET') {
} else if ($method == 'GET') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
$url = sprintf("%s?%s", $url, http_build_query($data));
}
else if($method == 'PUT') {
} else if ($method == 'PUT') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
else if($method == 'DELETE') {
} else if ($method == 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
}
else if($method == 'PATCH') {
} else if ($method == 'PATCH') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
else {
} else {
throw new Exception('Method not allowed');
}
@ -990,75 +980,75 @@ class ChatController extends BaseController{
}
return $result;
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Error: ' . $e->getMessage();
}
}
private function exec_php($code) {
private function exec_php($code)
{
try {
ob_start();
eval("?>" . $code . "<?php ");
$result = ob_get_clean();
if($result) {
if ($result) {
return 'The PHP code has been successfully executed. Result: ' . json_encode($result);
}
else {
} else {
return 'Sorry, I couldn\'t execute the PHP code.';
}
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t execute the PHP code. Error: ' . $e->getMessage();
}
}
private function get_current_user() {
private function get_current_user()
{
return json_encode(Auth::user());
}
private function get_user_by_id($id) {
private function get_user_by_id($id)
{
$user = User::find($id);
if($user) {
if ($user) {
return json_encode($user);
}
else {
} else {
return 'Sorry, I couldn\'t find the user with the id ' . $id . '. Try searching information in files.';
}
}
private function find_users_by_name($name) {
private function find_users_by_name($name)
{
$users = User::where('username', 'LIKE', '%' . $name . '%')->get();
if($users) {
if ($users) {
return json_encode($users);
}
else {
} else {
return 'Sorry, I couldn\'t find the users with the name ' . $name . '. Try searching information in files.';
}
}
private function list_users() {
private function list_users()
{
$users = User::get();
if($users) {
if ($users) {
return json_encode($users);
}
else {
} else {
return 'Sorry, I couldn\'t list the users. Try searching information in files.';
}
}
private function clear_cache() {
private function clear_cache()
{
Cache::clear();
return 'The cache has been successfully cleared.';
}
private function send_mail($to, $subject, $html_message) {
private function send_mail($to, $subject, $html_message)
{
try {
return "Email sent. Result: " . json_encode((new CustomEmail($to, $subject, $html_message))->send());
}
catch(Exception $e) {
} catch (Exception $e) {
return 'Sorry, I couldn\'t send the email. Error: ' . $e->getMessage();
}
}
}
}