diff --git a/app/Http/Controllers/Api/ChatbotController.php b/app/Http/Controllers/Api/ChatbotController.php index a804f79..0f95776 100644 --- a/app/Http/Controllers/Api/ChatbotController.php +++ b/app/Http/Controllers/Api/ChatbotController.php @@ -7,6 +7,7 @@ use App\Services\OpenAIService; use App\Http\Controllers\Controller; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; class ChatbotController extends Controller { @@ -19,7 +20,6 @@ class ChatbotController extends Controller public function generateText(Request $request) { - info($request); $request->validate([ 'tab_active' => 'required|string', 'prompt' => 'required|string', @@ -33,47 +33,29 @@ class ChatbotController extends Controller default => "UNKNOWN", }; + $chatHistory = $request->input('chatHistory'); + // Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]); + if ($main_content === "UNKNOWN") { return response()->json(['response' => 'Invalid tab_active value.'], 400); } - info($main_content); + // info($main_content); - // Klasifikasi apakah pertanyaan butuh database atau bisa dijawab langsung - $classifyResponse = $this->openAIService->generateClassifyMainContent($request->input('prompt'), $main_content); + $queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content, $chatHistory); + + $firstValidation = $this->openAIService->validateSyntaxQuery($queryResponse); + $secondValidation = $this->openAIService->validateSyntaxQuery($queryResponse); - if ($classifyResponse === "DATABASE") { - $queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content); - if (is_array($queryResponse)) { - info('Query Response is an array: ', $queryResponse); - } else { - info('Query Response is a string: ' . $queryResponse); - } - - // Validasi query dua kali sebelum eksekusi - if ( - $this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" && - $this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" - ) { - info($queryResponse); - $queryResponse = str_replace(['```sql', '```'], '', $queryResponse); - $resultQuery = DB::select($queryResponse); - $formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT); - $nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery); - $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); - return response()->json(['response' => $finalGeneratedText]); - } - - return response()->json(['response' => ''], 400); - } - - if ($classifyResponse === "GENERAL") { - $nlpResult = $this->openAIService->generateGeneralText($request->input('prompt'), $main_content); - $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); - return response()->json(['response' => $finalGeneratedText]); - } - - return response()->json(['response' => ''], 500); + $formattedResultQuery = "[]"; + $queryResponse = str_replace(['```sql', '```'], '', $queryResponse); + $resultQuery = DB::select($queryResponse); + $formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT); + // info($formattedResultQuery); + + $nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery); + $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); + return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]); } public function mainGenerateText(Request $request) @@ -104,7 +86,10 @@ class ChatbotController extends Controller ], 400); } - $queryResponse = $this->openAIService->createMainQuery($classifyResponse, $request->input('prompt')); + $chatHistory = $request->input('chatHistory'); + Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]); + + $queryResponse = $this->openAIService->createMainQuery($classifyResponse, $request->input('prompt'), $chatHistory); info($queryResponse); $firstValidation = $this->openAIService->validateSyntaxQuery($queryResponse); @@ -112,18 +97,15 @@ class ChatbotController extends Controller $formattedResultQuery = "[]"; - if($firstValidation === "VALID" && $secondValidation === "VALID") - { - $queryResponse = str_replace(['```sql', '```'], '', $queryResponse); - $queryResult = DB::select($queryResponse); - info($queryResult); - $formattedResultQuery = json_encode($queryResult, JSON_PRETTY_PRINT); - info($formattedResultQuery); - } + $queryResponse = str_replace(['```sql', '```'], '', $queryResponse); + $queryResult = DB::select($queryResponse); + + $formattedResultQuery = json_encode($queryResult, JSON_PRETTY_PRINT); + $nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery); $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); - return response()->json(['response' => $finalGeneratedText]); + return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]); } catch (\Exception $e) { // Tangani error dan log exception \Log::error("Error generating text: " . $e->getMessage()); diff --git a/app/Services/OpenAIService.php b/app/Services/OpenAIService.php index ef8bbc3..25626b4 100644 --- a/app/Services/OpenAIService.php +++ b/app/Services/OpenAIService.php @@ -4,6 +4,7 @@ namespace App\Services; use OpenAI; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Log; class OpenAIService { @@ -11,54 +12,11 @@ class OpenAIService public function __construct() { + // $this->client = OpenAI::client(env('OPENAI_API_KEY')); $this->client = OpenAI::client(env('OPENAI_API_KEY')); } - public function generateGeneralText($prompt, $mainContent) - { - $response = $this->client->chat()->create([ - 'model' => 'gpt-4o-mini', - 'messages' => [ - [ - 'role' => 'system', - 'content' => "You are an expert assistant. Your task is to generate a concise response based on the provided prompt and main content. - - Guidelines: - - Summarize the key points in exactly 5 bullet points. - - Ensure the response is clear and relevant to the prompt. - - Use simple and professional language." - ], - ['role' => 'user', 'content' => "Prompt: $prompt \nMain Content: $mainContent"], - ], - ]); - - return trim($response['choices'][0]['message']['content'] ?? 'No response'); - } - - public function generateClassifyMainContent($prompt, $mainContent) - { - $response = $this->client->chat()->create([ - 'model' => 'gpt-4o-mini', - 'messages' => [ - [ - 'role' => 'system', - 'content' => "You are an expert assistant in classifying questions based on whether their answers must be retrieved from a database or can be explained generally. - Your task is to return one of the following two labels: - - \"DATABASE\" → If the question requires specific data that can only be obtained from a database. - - \"GENERAL\" → If the question can be answered without accessing a database. - - Consider the following context: \"$mainContent\" - - Respond with only one of the labels: \"DATABASE\" or \"GENERAL\"." - ], - ['role' => 'user', 'content' => $prompt], - ], - ]); - - return trim($response['choices'][0]['message']['content'] ?? 'No response'); - } - - public function generateQueryBasedMainContent($prompt, $mainContent) + public function generateQueryBasedMainContent($prompt, $mainContent, $chatHistory) { // Load file JSON $jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path @@ -72,17 +30,59 @@ class OpenAIService // Ambil template berdasarkan kategori $promptTemplate = $jsonData[$mainContent]['prompt']; + // Menyusun pesan untuk OpenAI + $messages = [ + ['role' => 'system', 'content' => $promptTemplate], + ]; + + // Menambahkan chat history sebagai konteks + foreach ($chatHistory as $chat) { + if (isset($chat['user'])) { + $messages[] = ['role' => 'user', 'content' => $chat['user']]; + } + if (isset($chat['rawBotResponse'])) { + $messages[] = ['role' => 'assistant', 'content' => $chat['rawBotResponse']]; + } + } + + // Tambahkan prompt terbaru user + $messages[] = ['role' => 'user', 'content' => $prompt]; + + // Kirim request ke OpenAI API $response = $this->client->chat()->create([ 'model' => 'gpt-4o-mini', - 'messages' => [ - ['role' => 'system', 'content' => $promptTemplate], - ['role' => 'user', 'content' => $prompt], - ], + 'messages' => $messages, ]); return trim($response['choices'][0]['message']['content'] ?? 'No response'); } + + // public function generateQueryBasedMainContent($prompt, $mainContent, $chatHistory) + // { + // // Load file JSON + // $jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path + // $jsonData = json_decode(file_get_contents($jsonPath), true); + + // // Periksa apakah kategori ada dalam JSON + // if (!isset($jsonData[$mainContent])) { + // return "Template prompt tidak ditemukan."; + // } + + // // Ambil template berdasarkan kategori + // $promptTemplate = $jsonData[$mainContent]['prompt']; + + // $response = $this->client->chat()->create([ + // 'model' => 'gpt-4o-mini', + // 'messages' => [ + // ['role' => 'system', 'content' => $promptTemplate], + // ['role' => 'user', 'content' => $prompt], + // ], + // ]); + + // return trim($response['choices'][0]['message']['content'] ?? 'No response'); + // } + public function validateSyntaxQuery($queryResponse) { @@ -181,8 +181,8 @@ class OpenAIService return trim($response['choices'][0]['message']['content'] ?? 'No response'); } - - public function createMainQuery($classify, $prompt) + + public function createMainQuery($classify, $prompt, $chatHistory) { // Load file JSON $jsonPath = public_path('templates/table_config.json'); @@ -197,54 +197,80 @@ class OpenAIService $tableName = $jsonConfig[$classify]['table_name']; $columns = implode(', ', $jsonConfig[$classify]['list_column']); + // Konversi chatHistory ke dalam format messages + $messages = [ + [ + 'role' => 'system', + 'content' => "You are an AI assistant that generates only valid MariaDB queries based on user requests. + Use the following table information to construct the SQL query: + + - Table Name: $tableName + - Available Columns: $columns + + Generate only the SQL query without any explanation or additional text. + The query should include `LIMIT 10` to restrict the results." + ] + ]; + + // Menambahkan chat history sebagai konteks + foreach ($chatHistory as $chat) { + if (isset($chat['user'])) { + $messages[] = ['role' => 'user', 'content' => $chat['user']]; + } + if (isset($chat['rawBotResponse'])) { + $messages[] = ['role' => 'assistant', 'content' => $chat['rawBotResponse']]; + } + } + + // Tambahkan prompt utama pengguna + $messages[] = ['role' => 'user', 'content' => $prompt]; + + // Kirim permintaan ke model AI $response = $this->client->chat()->create([ 'model' => 'gpt-4o-mini', - 'messages' => [ - [ - 'role' => 'system', - 'content' => "You are an AI assistant that generates only valid MariaDB queries based on user requests. - Use the following table information to construct the SQL query: - - - Table Name: $tableName - - Available Columns: $columns - - Generate only the SQL query without any explanation or additional text." - ], - [ - 'role' => 'user', - 'content' => $prompt - ], - ], + 'messages' => $messages ]); - + return trim($response['choices'][0]['message']['content'] ?? 'No response'); } - - public function mainGenerateText($prompt) { - $response = $this->client->chat()->create([ - 'model' => 'gpt-4o-mini', - 'messages' => [ - [ - 'role' => 'system', - 'content' => "You are an expert assistant with deep knowledge in multiple fields, including programming, data science, and business strategy. Provide clear, concise, and accurate responses." - ], - [ - 'role' => 'user', - 'content' => $prompt - ], - ], - ]); - - return trim($response['choices'][0]['message']['content'] ?? 'No response'); - } - - // 1. Buat fungsi untuk akses data advertisements - // 2. Buat fungsi untuk akses data business_or_industries - // 3. Buat fungsi untuk akses data customers - // 4. Buat fungsi untuk akses data pbg_task - // 5. Buat fungsi untuk akses data pbg_task_retribution - // 6. Buat fungsi untuk akses data spatial_plannings - // 7. Buat fungsi untuk akses data tourisms - // 8. Buat fungsi untuk akses data umkms + + // public function createMainQuery($classify, $prompt) + // { + // // Load file JSON + // $jsonPath = public_path('templates/table_config.json'); + // $jsonConfig = json_decode(file_get_contents($jsonPath), true); + + // // Pastikan kategori tersedia dalam konfigurasi + // if (!isset($jsonConfig[$classify])) { + // return "Error: Kategori tidak ditemukan dalam konfigurasi."; + // } + + // // Ambil nama tabel dan kolom + // $tableName = $jsonConfig[$classify]['table_name']; + // $columns = implode(', ', $jsonConfig[$classify]['list_column']); + + // $response = $this->client->chat()->create([ + // 'model' => 'gpt-4o-mini', + // 'messages' => [ + // [ + // 'role' => 'system', + // 'content' => "You are an AI assistant that generates only valid MariaDB queries based on user requests. + // Use the following table information to construct the SQL query: + + // - Table Name: $tableName + // - Available Columns: $columns + + // Generate only the SQL query without any explanation or additional text + // The query should include `LIMIT 10` to restrict the results." + // ], + // [ + // 'role' => 'user', + // 'content' => $prompt + // ], + // ], + // ]); + + // return trim($response['choices'][0]['message']['content'] ?? 'No response'); + // } } diff --git a/public/templates/contentTemplatePrompt.json b/public/templates/contentTemplatePrompt.json index 938f381..d217ea7 100644 --- a/public/templates/contentTemplatePrompt.json +++ b/public/templates/contentTemplatePrompt.json @@ -1,11 +1,11 @@ { "RETRIBUTION": { - "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results." }, "DOCUMENT VALIDATION": { - "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results." }, "DATA SUMMARY": { - "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results." } } \ No newline at end of file diff --git a/resources/js/chatbot-pimpinan/index.js b/resources/js/chatbot-pimpinan/index.js index dddff96..6f90345 100644 --- a/resources/js/chatbot-pimpinan/index.js +++ b/resources/js/chatbot-pimpinan/index.js @@ -10,6 +10,7 @@ document.addEventListener("DOMContentLoaded", function () { const textarea = document.getElementById("user-message"); const sendButton = document.getElementById("send"); const conversationArea = document.querySelector(".row.flex-grow"); + const chatHistory = []; // Fungsi untuk mengirim pesan async function sendMessage() { @@ -30,7 +31,7 @@ document.addEventListener("DOMContentLoaded", function () { } // Panggil API untuk mendapatkan respons dari bot - const botResponse = await getBotResponse(userText); + const botResponse = await getBotResponse(userText, chatHistory); // Perbarui pesan bot dengan respons yang sebenarnya if (messageTextContainer) { @@ -124,12 +125,12 @@ document.addEventListener("DOMContentLoaded", function () { } // Fungsi untuk memanggil API - async function getBotResponse(userText) { + async function getBotResponse(userText, historyChat) { try { const url = `${GlobalConfig.apiHost}/api/main-generate-text`; const response = await fetch(url, { method: "POST", - body: JSON.stringify({prompt: userText }), + body: JSON.stringify({prompt: userText, chatHistory: historyChat}), headers: { Authorization: `Bearer ${document .querySelector('meta[name="api-token"]') @@ -139,6 +140,12 @@ document.addEventListener("DOMContentLoaded", function () { }); const data = await response.json(); + const rawBotResponse = data.nlpResponse; + // Tambahkan ke chatHistory + chatHistory.push({ + user: userText, + rawBotResponse: rawBotResponse, + }); return data.response || "Maaf, saya tidak mengerti."; } catch (error) { console.error("Error fetching bot response:", error); diff --git a/resources/js/chatbot/index.js b/resources/js/chatbot/index.js index 8e157d6..5a63924 100644 --- a/resources/js/chatbot/index.js +++ b/resources/js/chatbot/index.js @@ -32,6 +32,32 @@ document.addEventListener("DOMContentLoaded", function () { setTimeout(() => { const tab_active = getActiveTabId(); console.log("Active Tab ID:", tab_active); + + // Hapus semua chat kecuali pesan default bot + conversationArea.innerHTML = ` +
+ +
+ avatar-3 +
+ + +
+ +

Neng Bedas

+ + +
+

Halo! Ada yang bisa saya bantu?

+ + +
+

Now

+
+
+
+
+ `; }, 100); // Timeout untuk memastikan class `active` sudah diperbarui }); }); @@ -39,6 +65,7 @@ document.addEventListener("DOMContentLoaded", function () { const textarea = document.getElementById("user-message"); const sendButton = document.getElementById("send"); const conversationArea = document.querySelector(".row.flex-grow"); + const chatHistory = []; // Fungsi untuk mengirim pesan async function sendMessage() { @@ -62,7 +89,7 @@ document.addEventListener("DOMContentLoaded", function () { } // Panggil API untuk mendapatkan respons dari bot - const botResponse = await getBotResponse(currentTab, userText); + const botResponse = await getBotResponse(currentTab, userText, chatHistory); // Perbarui pesan bot dengan respons yang sebenarnya if (messageTextContainer) { @@ -156,12 +183,12 @@ document.addEventListener("DOMContentLoaded", function () { } // Fungsi untuk memanggil API - async function getBotResponse(tab_active, userText) { + async function getBotResponse(tab_active, userText, historyChat) { try { const url = `${GlobalConfig.apiHost}/api/generate-text`; const response = await fetch(url, { method: "POST", - body: JSON.stringify({tab_active:tab_active, prompt: userText }), + body: JSON.stringify({tab_active:tab_active, prompt: userText, chatHistory: historyChat }), headers: { Authorization: `Bearer ${document .querySelector('meta[name="api-token"]') @@ -171,6 +198,12 @@ document.addEventListener("DOMContentLoaded", function () { }); const data = await response.json(); + const rawBotResponse = data.nlpResponse; + // Tambahkan ke chatHistory + chatHistory.push({ + user: userText, + rawBotResponse: rawBotResponse, + }); return data.response || "Maaf, saya tidak mengerti."; } catch (error) { console.error("Error fetching bot response:", error);