├── .env.example ├── .gitignore ├── README.md ├── db.js ├── jest.config.js ├── models └── conversation.js ├── openAiService.js ├── openAiService_test_result.json ├── package-lock.json ├── package.json ├── public ├── app.js ├── app.js or other JS file responsible for UI events ├── conversationHandler.js ├── displayRealTimeResponse.js ├── displayResponses.js ├── eventBindings.js ├── favicon.ico ├── index.html ├── js │ ├── bindEvents.js │ ├── conversationDetailsHandler.js │ ├── conversationHandler.js │ ├── displayRealTimeResponse.js │ ├── eventBindings.js │ ├── eventHandlers.js │ ├── messageItems.js │ ├── messageItemsHandler.js │ ├── responseDeletionHandler.js │ └── submissionHandler.js ├── messageItems.js ├── openAiRequestHandler.js ├── responseDeletionHandler.js ├── responseDisplayHandler.js ├── responseHandler.js ├── submissionDisplayHandler.js └── submissionHandler.js ├── server.js ├── testOpenAiService.js └── tests ├── openAiService.test.js └── sample.test.js /.env.example: -------------------------------------------------------------------------------- 1 | MONGODB_URI= 2 | OPENAI_API_KEY= 3 | LLM_MODEL= 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | .gpt-pilot -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prompt Engineering App 2 | 3 | ## Overview 4 | 5 | Prompt Engineering is a web application designed to interact with and analyze responses from the OpenAI API. Users can construct a dialogue with an AI large language model (LLM) and submit this dialogue for processing. The app then sends the dialogue to the OpenAI API multiple times in parallel, depending on the user's input, and streams the results back in real-time. 6 | 7 | ## Features 8 | 9 | - Interactive interface to construct dialogues between a user and an assistant. 10 | - Capability to add infinite message items dynamically. 11 | - Option to determine the roles of messages as either from the user or assistant. 12 | - Submit the dialogue for concurrent processing a specified number of times (0-100). 13 | - Real-time streaming and display of responses from the OpenAI API. 14 | 15 | ## Installation 16 | 17 | Before installing, ensure Node.js is installed on your system. 18 | 19 | To install the app, follow these steps: 20 | 21 | 1. Clone the repository to your local machine. 22 | 2. Install dependencies by running `npm install` in the project root directory. 23 | 3. Start the server with `npm start`. The application will be served at `http://localhost:3000`. 24 | 25 | ## Configuration 26 | 27 | Create a `.env` file in the root directory with the following format: 28 | 29 | ``` 30 | OPENAI_API_KEY=your-actual-openai-api-key 31 | MONGODB_URI=mongodb://localhost:27017/prompt_engineering 32 | LLM_MODEL=gpt-4-turbo-preview 33 | ``` 34 | 35 | Replace `your-actual-openai-api-key` with your OpenAI API key. Ensure that MongoDB is running and accessible at the URI provided. 36 | 37 | ## Usage 38 | 39 | Access the web interface to construct your dialogue. Input your dialogue as a series of messages and assign roles to each message. Specify the number of times the dialogue should be sent to the OpenAI API and hit the 'SUBMIT' button to initiate the process. The responses will appear as expandable elements in real-time as they are streamed back from the API. 40 | 41 | ## Technologies 42 | 43 | - Node.js 44 | - Express 45 | - Socket.IO for real-time communication. 46 | - MongoDB for storing dialogues. 47 | - Tailwind CSS for styling. 48 | 49 | ## Testing 50 | 51 | Run `npm test` to execute the test suites included in the project. 52 | 53 | ## Contributions 54 | 55 | Contributions to the project are welcome. Please ensure you write tests for new features and run existing tests before making a pull request. 56 | 57 | ## License 58 | 59 | This project is licensed under the ISC License - see the LICENSE file for details. 60 | -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const MONGO_URI = process.env.MONGODB_URI; 4 | 5 | const connectDB = async () => { 6 | try { 7 | await mongoose.connect(MONGO_URI, { 8 | useNewUrlParser: true, 9 | useUnifiedTopology: true 10 | }); 11 | console.log('MongoDB Connected...'); 12 | } catch (err) { 13 | console.error('MongoDB Connection Error:', err); 14 | process.exit(1); 15 | } 16 | }; 17 | 18 | module.exports = connectDB; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testRunner: 'jest-circus/runner', 3 | testEnvironment: 'node', 4 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$', 5 | verbose: true, 6 | transform: {}, 7 | }; -------------------------------------------------------------------------------- /models/conversation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const conversationSchema = new Schema({ 6 | name: { 7 | type: String, 8 | required: true 9 | }, 10 | messages: [ 11 | { 12 | text: String, 13 | role: { 14 | type: String, 15 | enum: ['user', 'assistant', 'system'], 16 | required: true 17 | } 18 | } 19 | ], 20 | responses: [ 21 | { 22 | timestamp: { 23 | type: Date, 24 | default: Date.now 25 | }, 26 | text: String 27 | } 28 | ] 29 | }, { timestamps: true }); 30 | 31 | const Conversation = mongoose.model('Conversation', conversationSchema); 32 | 33 | console.log("Conversation model loaded successfully."); // gpt_pilot_debugging_log 34 | 35 | module.exports = Conversation; 36 | -------------------------------------------------------------------------------- /openAiService.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const OPENAI_API_BASE_URL = 'https://api.openai.com/v1'; 4 | const OPENAI_API_KEY = process.env.OPENAI_API_KEY; 5 | const LLM_MODEL = process.env.LLM_MODEL; 6 | 7 | // Exponential backoff configuration 8 | const MAX_RETRIES = 5; 9 | const INITIAL_BACKOFF_DELAY = 1000; 10 | const BACKOFF_MULTIPLIER = 2; 11 | 12 | // Utility function to format messages for OpenAI API 13 | const formatMessagesForOpenAi = (messages) => { 14 | // Enhanced function to handle 'system' messages correctly 15 | return messages.map(message => { 16 | switch (message.role) { 17 | case 'system': 18 | console.log('Processing system message:', message.text); // gpt_pilot_debugging_log 19 | // Though OpenAI's API may not directly use 'system' roles, this prepares them for potential logging or debugging 20 | return { role: 'system', content: message.text.trim() }; 21 | case 'assistant': 22 | console.log('Formatting message as assistant role:', message.text); // gpt_pilot_debugging_log 23 | return { role: 'system', content: message.text.trim() }; // Treating 'assistant' as 'system' for OpenAI 24 | case 'user': 25 | console.log('Formatting message as user role:', message.text); // gpt_pilot_debugging_log 26 | return { role: 'user', content: message.text.trim() }; 27 | default: 28 | console.error('Encountered unknown role type:', message.role); // gpt_pilot_debugging_log 29 | throw new Error(`Unknown role type: ${message.role}`); 30 | } 31 | }); 32 | }; 33 | 34 | const callOpenAiApi = async (messages, io, requestId) => { 35 | if (!OPENAI_API_KEY) { 36 | console.error('OpenAI API key is not set.'); 37 | throw new Error('OpenAI API key is not set.'); 38 | } 39 | 40 | const OPENAI_COMPLETIONS_ENDPOINT = `${OPENAI_API_BASE_URL}/chat/completions`; 41 | 42 | // Using the updated utility function to format messages 43 | const messagesPayload = formatMessagesForOpenAi(messages); 44 | 45 | let attempt = 0; 46 | let backoffDelay = INITIAL_BACKOFF_DELAY; 47 | 48 | while (attempt < MAX_RETRIES) { 49 | try { 50 | const response = await axios.post( 51 | OPENAI_COMPLETIONS_ENDPOINT, 52 | { 53 | model: LLM_MODEL, 54 | messages: messagesPayload, 55 | }, 56 | { 57 | headers: { 58 | 'Authorization': `Bearer ${OPENAI_API_KEY}`, 59 | 'Content-Type': 'application/json', 60 | }, 61 | } 62 | ); 63 | 64 | const responseData = response.data; 65 | console.log(`Success with request ID: ${requestId}, response:`, responseData); // Debug log for response data. gpt_pilot_debugging_log 66 | io.emit('openai_real_response', { response: responseData, requestId: requestId }); 67 | return; 68 | } catch (error) { 69 | console.error(`Error with request ID: ${requestId}, error message:`, error.message, 'error response:', error.response?.data, error.stack); // Logging the full error details. gpt_pilot_debugging_log 70 | if (error.response?.status === 429 && attempt < MAX_RETRIES) { 71 | // Rate limit error, wait before retrying 72 | await new Promise(resolve => setTimeout(resolve, backoffDelay)); 73 | backoffDelay *= BACKOFF_MULTIPLIER; 74 | attempt++; 75 | } else { 76 | // Other type of error or max retries exceeded 77 | const errorData = { 78 | message: error.response?.data?.error?.message ?? "Unknown error occurred", 79 | status: error.response?.status ?? 500, 80 | requestId: requestId, 81 | }; 82 | io.emit('openai_error', errorData); 83 | return; 84 | } 85 | } 86 | } 87 | }; 88 | 89 | module.exports = { callOpenAiApi }; 90 | -------------------------------------------------------------------------------- /openAiService_test_result.json: -------------------------------------------------------------------------------- 1 | {"numFailedTestSuites":1,"numFailedTests":0,"numPassedTestSuites":0,"numPassedTests":0,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":1,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":0,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1706273998132,"success":false,"testResults":[{"assertionResults":[],"coverage":{},"endTime":1706273999099,"message":" ● Test suite failed to run\n\n Jest encountered an unexpected token\n\n This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.\n\n By default, if Jest sees a Babel config, it will use that to transform your files, ignoring \"node_modules\".\n\n Here's what you can do:\n • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.\n • To have some of your \"node_modules\" files transformed, you can specify a custom \"transformIgnorePatterns\" in your config.\n • If you need a custom transformation specify a \"transform\" option in your config.\n • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the \"moduleNameMapper\" config option.\n\n You'll find more details and examples of these config options in the docs:\n https://jestjs.io/docs/en/configuration.html\n\n Details:\n\n C:\\Users\\spesna_5\\Desktop\\gpt\\gpt-pilot\\workspace\\Prompt_engineering\\node_modules\\axios\\index.js:1\n ({\"Object.\":function(module,exports,require,__dirname,__filename,global,jest){import axios from './lib/axios.js';\n ^^^^^^\n\n SyntaxError: Cannot use import statement outside a module\n\n > 1 | const axios = require('axios');\n | ^\n 2 | const MockAdapter = require('axios-mock-adapter');\n 3 | const { callOpenAiApi } = require('../openAiService');\n 4 | const { Server } = require('socket.io');\n\n at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)\n at Object. (tests/openAiService.test.js:1:104)\n","name":"C:\\Users\\spesna_5\\Desktop\\gpt\\gpt-pilot\\workspace\\Prompt_engineering\\tests\\openAiService.test.js","startTime":1706273999099,"status":"failed","summary":""}],"wasInterrupted":false} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prompt_engineering", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "directories": { 6 | "test": "tests" 7 | }, 8 | "scripts": { 9 | "test": "jest", 10 | "start": "node server.js" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "axios": "^1.6.6", 17 | "dotenv": "^16.4.1", 18 | "express": "^4.18.2", 19 | "mongoose": "^8.1.1", 20 | "socket.io": "^4.7.4" 21 | }, 22 | "description": "", 23 | "devDependencies": { 24 | "@jest/types": "^26.6.2", 25 | "axios-mock-adapter": "^1.22.0", 26 | "jest": "^26.6.0", 27 | "jest-circus": "^26.6.0", 28 | "socket.io-client": "^4.7.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | // Remove message item function moved to global scope 2 | function removeMessageItem(element) { 3 | try { 4 | const messageItem = element.closest('div'); 5 | messageItem.remove(); 6 | console.log('Message item removed successfully'); 7 | } catch (error) { 8 | console.error('Failed to remove message item:', error.message, error.stack); 9 | } 10 | } 11 | 12 | // Updated to handle DELETE request with the correct conversationId and responseId 13 | function deleteResponse(event, conversationId, responseId) { 14 | event.preventDefault(); 15 | console.log(`Initiating DELETE request to URL: /api/delete-response/${conversationId}/${responseId}.`, { conversationId, responseId }); // gpt_pilot_debugging_log 16 | 17 | fetch(`/api/delete-response/${conversationId}/${responseId}`, { 18 | method: 'DELETE' 19 | }) 20 | .then(response => { 21 | console.log(`Deletion API response status for Conversation ID: ${conversationId}, Response ID: ${responseId}: ${response.status}`); // gpt_pilot_debugging_log 22 | if (!response.ok) { 23 | throw new Error(`HTTP error! Status: ${response.status}`); 24 | } 25 | const responseElement = document.getElementById(`response-${responseId}`); 26 | if (responseElement) { 27 | responseElement.remove(); 28 | console.log(`Response #${responseId} deleted successfully from the DOM.`); // gpt_pilot_debugging_log 29 | } else { 30 | console.error(`Could not find response element #${responseId} in the DOM to remove.`); // gpt_pilot_debugging_log 31 | } 32 | }) 33 | .catch(error => { 34 | console.error('Failed to delete response:', error.message, error.stack, { conversationId, responseId }); // gpt_pilot_debugging_log 35 | alert(`Failed to delete response: ${error.message}`); 36 | }); 37 | } 38 | 39 | document.addEventListener('DOMContentLoaded', () => { 40 | // Add event listener for the SUBMIT button 41 | const submitButton = document.getElementById('submitButton'); 42 | if (submitButton) { 43 | submitButton.addEventListener('click', submitConversation); 44 | } else { 45 | console.error('Submit button not found'); 46 | } 47 | 48 | // Refactor for event delegation for delete response 49 | const responseContainer = document.getElementById('responseContainer'); 50 | 51 | if (responseContainer) { 52 | responseContainer.addEventListener('click', function(event) { 53 | if (event.target.matches('.delete-btn')) { 54 | const button = event.target; 55 | const requestId = button.getAttribute('data-request-id'); 56 | const responseId = button.getAttribute('data-response-id'); 57 | deleteResponse(event, requestId, responseId); 58 | } 59 | }); 60 | } else { 61 | console.error('Response container not found for event delegation.'); 62 | } 63 | 64 | // Other functionalities remain unchanged 65 | 66 | function addMessageItem(text = '', role = 'user') { 67 | const messageListElement = document.getElementById('messageList'); 68 | const newMessageItem = document.createElement('div'); 69 | newMessageItem.classList.add('flex', 'items-center', 'mb-2', 'bg-white', 'border', 'p-2', 'rounded', 'shadow'); 70 | newMessageItem.innerHTML = ` 71 | 72 | 77 | 78 | `; 79 | messageListElement.appendChild(newMessageItem); 80 | console.log('Added new message item to list'); 81 | } 82 | 83 | const addMessageButton = document.getElementById('addMessageButton'); 84 | if (addMessageButton) { 85 | addMessageButton.addEventListener('click', () => { 86 | try { 87 | addMessageItem(); 88 | console.log('New message item added.'); 89 | } catch (error) { 90 | console.error('Failed to add new message item:', error.message, error.stack); 91 | } 92 | }); 93 | } else { 94 | console.error('Add Message button not found'); 95 | } 96 | 97 | // Add initial message item on load 98 | addMessageItem(); 99 | 100 | // Function to load messages from JSON 101 | function loadMessagesFromJson() { 102 | try { 103 | const jsonTextarea = document.getElementById('jsonImportTextarea'); 104 | const messages = JSON.parse(jsonTextarea.value); 105 | const messageList = document.getElementById('messageList'); 106 | messageList.innerHTML = ''; // Clear existing messages 107 | 108 | messages.forEach(message => { 109 | addMessageItem(message.text, message.role); // Reuse function to add messages to the UI 110 | }); 111 | console.log('Messages loaded from JSON.'); // Logging successful operation 112 | } catch (error) { 113 | // Proper error handling and logging with stack trace 114 | console.error('Failed to load messages from JSON:', error.message, error.stack); 115 | // Inform the user through the UI that there was an error 116 | const jsonErrorElement = document.getElementById('jsonError'); 117 | if (jsonErrorElement) { 118 | jsonErrorElement.textContent = error.message; 119 | jsonErrorElement.classList.remove('hidden'); 120 | } else { 121 | // If the error display element doesn't exist, log the error in the console 122 | console.error('JSON error display element not found in the UI.'); 123 | } 124 | } 125 | } 126 | 127 | // Event listener for the Load Messages button 128 | const loadMessagesButton = document.getElementById('loadMessagesButton'); 129 | if (loadMessagesButton) { 130 | loadMessagesButton.addEventListener('click', loadMessagesFromJson); 131 | console.log('Event listener for Load Messages button added.'); // Logging successful operation 132 | } else { 133 | // Error handling with proper logging if the element is not found 134 | console.error('Load Messages button not found'); 135 | } 136 | 137 | // Function to handle OpenAI API errors 138 | function handleOpenAiError(error) { 139 | console.error('OpenAI Error:', error.message, error.stack); 140 | } 141 | 142 | // Function to display responses in real-time 143 | function displayRealTimeResponse(data) { 144 | const responseContainer = document.getElementById('responseContainer'); 145 | const { response, requestId } = data; 146 | const responseElement = document.createElement('details'); 147 | responseElement.id = `response-${requestId}`; 148 | 149 | const deleteButton = document.createElement('button'); 150 | deleteButton.classList.add('ml-2', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded'); 151 | deleteButton.textContent = 'Delete'; 152 | console.log(`Attempting to attach delete event listener. Request ID: ${requestId}, Response ID: ${response.id}`); // gpt_pilot_debugging_log 153 | deleteButton.setAttribute('data-request-id', requestId); 154 | deleteButton.setAttribute('data-response-id', response.id); 155 | 156 | responseElement.innerHTML = ` 157 | Response #${requestId} 158 |

${response.choices[0].message.content}

159 | `; 160 | responseElement.appendChild(deleteButton); 161 | 162 | responseContainer.appendChild(responseElement); 163 | console.log(`Response #${requestId} displayed`); 164 | } 165 | 166 | // Function to collect messages and make a POST request on submit 167 | async function submitConversation() { 168 | try { 169 | const messageElements = document.querySelectorAll('#messageList > div'); 170 | const messages = Array.from(messageElements).map(messageElement => { 171 | const textArea = messageElement.querySelector('textarea'); 172 | const roleSelect = messageElement.querySelector('select'); 173 | return { 174 | text: textArea.value, 175 | role: roleSelect.value 176 | }; 177 | }); 178 | const apiRequestsCount = parseInt(document.getElementById('apiRequestsCount').value, 10) || 0; 179 | if (Number.isNaN(apiRequestsCount) || apiRequestsCount < 0 || apiRequestsCount > 100) { 180 | throw new Error('The number of API requests must be between 0 and 100.'); 181 | } 182 | if (socket === null) { 183 | console.log('Establishing WebSocket connection.'); 184 | socket = io(); 185 | socket.on('openai_real_response', displayRealTimeResponse); 186 | socket.on('openai_error', handleOpenAiError); 187 | } 188 | const response = await fetch('/submit', { 189 | method: 'POST', 190 | headers: { 191 | 'Content-Type': 'application/json', 192 | }, 193 | body: JSON.stringify({ messages, apiRequestsCount }), 194 | }); 195 | if (!response.ok) { 196 | throw new Error(`HTTP error! Status: ${response.status}`); 197 | } 198 | console.log('Conversation submitted successfully.'); 199 | } catch (error) { 200 | console.error('Failed to submit conversation:', error.message, error.stack); 201 | } 202 | } 203 | 204 | // Add event listener to the Save Conversation button 205 | const saveConversationButton = document.getElementById('saveConversationButton'); 206 | if (saveConversationButton) { 207 | saveConversationButton.addEventListener('click', saveConversation); 208 | } else { 209 | console.error('Save Conversation button not found'); 210 | } 211 | 212 | // Function to initiate the process of saving the current conversation state 213 | function saveConversation() { 214 | try { 215 | console.log('Initiating process to save the current conversation.'); 216 | // Collect message items 217 | const messageItems = document.querySelectorAll('#messageList > div'); 218 | const messages = Array.from(messageItems).map(item => { 219 | const textarea = item.querySelector('textarea'); 220 | const select = item.querySelector('select'); 221 | return { 222 | text: textarea.value, 223 | role: select.value 224 | }; 225 | }); 226 | 227 | // Collect OpenAI API responses from the UI 228 | const responseElements = document.querySelectorAll('#responseContainer > details'); 229 | const responses = Array.from(responseElements).map(detail => { 230 | const summary = detail.querySelector('summary'); 231 | const p = detail.querySelector('p'); 232 | return { 233 | text: p ? p.textContent : '', 234 | requestNumber: summary ? Number(summary.textContent.replace('Response #', '')) : null 235 | }; 236 | }); 237 | 238 | const conversationName = `Conversation_${new Date().toISOString()}`; 239 | const payload = { 240 | name: conversationName, 241 | messages: messages, 242 | responses: responses 243 | }; 244 | 245 | // Send the POST request to '/api/save-conversation' 246 | fetch('/api/save-conversation', { 247 | method: 'POST', 248 | headers: { 249 | 'Content-Type': 'application/json', 250 | }, 251 | body: JSON.stringify(payload), 252 | }) 253 | .then(response => { 254 | if (!response.ok) { 255 | throw new Error(`HTTP error! Status: ${response.status}`); 256 | } 257 | return response.json(); 258 | }) 259 | .then(data => { 260 | console.log('Conversation saved successfully:', data); 261 | alert('Conversation saved successfully!'); 262 | }) 263 | .catch(error => { 264 | console.error('Error saving conversation:', error.message, error.stack); 265 | alert(`Error saving conversation: ${error.message}`); 266 | }); 267 | } catch (error) { 268 | console.error('Failed to execute saveConversation function:', error.message, error.stack); 269 | alert(`Failed to save conversation: ${error.message}`); 270 | } 271 | } 272 | 273 | // Function to load and display a list of stored conversations 274 | function loadConversationsList() { 275 | console.log('Loading conversations list.'); // gpt_pilot_debugging_log 276 | fetch('/api/load-conversations', { 277 | method: 'GET', 278 | headers: { 279 | 'Content-Type': 'application/json', 280 | }, 281 | }) 282 | .then(response => { 283 | if (!response.ok) { 284 | throw new Error(`HTTP error! Status: ${response.status}`); 285 | } 286 | return response.json(); 287 | }) 288 | .then(conversations => { 289 | console.log('Conversations loaded:', conversations); // gpt_pilot_debugging_log 290 | const conversationsListContainer = document.getElementById('conversationsListContainer') || createConversationsListContainer(); 291 | conversationsListContainer.innerHTML = ''; // Clear previous conversation list 292 | 293 | conversations.forEach(conversation => { 294 | const listItem = document.createElement('button'); 295 | listItem.textContent = conversation.name; 296 | listItem.classList.add('block', 'text-left', 'p-2', 'hover:bg-gray-200', 'w-full', 'mt-1', 'relative'); 297 | 298 | // Delete button 299 | const deleteButton = document.createElement('button'); 300 | deleteButton.textContent = 'Delete'; 301 | deleteButton.classList.add('bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-1', 'px-2', 'rounded', 'absolute', 'right-0'); 302 | deleteButton.onclick = () => deleteConversation(conversation._id); 303 | listItem.appendChild(deleteButton); 304 | conversationsListContainer.appendChild(listItem); 305 | }); 306 | console.log('Conversations list loaded.'); 307 | }) 308 | .catch(error => { 309 | console.error('Failed to load conversations list:', error.message, error.stack); // gpt_pilot_debugging_log 310 | }); 311 | } 312 | 313 | // Function to delete a conversation 314 | function deleteConversation(conversationId) { 315 | fetch(`/api/delete-conversation/${conversationId}`, { 316 | method: 'DELETE' 317 | }) 318 | .then(response => { 319 | if (!response.ok) { 320 | throw new Error(`HTTP error! Status: ${response.status}`); 321 | } 322 | return response.json(); 323 | }) 324 | .then(data => { 325 | alert('Conversation deleted successfully.'); 326 | loadConversationsList(); // Refresh the list 327 | }) 328 | .catch(error => { 329 | console.error('Failed to delete conversation:', error.message, error.stack); 330 | alert(`Failed to delete conversation: ${error.message}`); 331 | }); 332 | } 333 | 334 | // Add event listener to Load Conversation button 335 | const loadConversationButton = document.getElementById('loadConversationButton'); 336 | if (loadConversationButton) { 337 | loadConversationButton.addEventListener('click', loadConversationsList); 338 | } else { 339 | console.error('Load Conversation button not found'); 340 | } 341 | 342 | // Function to load and display a specific conversation's details 343 | function loadConversationDetails(conversationId) { 344 | console.log(`Loading conversation details for ID: ${conversationId}`); 345 | fetch(`/api/load-conversations/${conversationId}`, { 346 | method: 'GET', 347 | headers: { 348 | 'Content-Type': 'application/json', 349 | }, 350 | }) 351 | .then(response => { 352 | if (!response.ok) { 353 | console.error(`HTTP error! Status: ${response.status}`); 354 | throw new Error(`HTTP error! Status: ${response.status}`); 355 | } 356 | return response.json(); 357 | }) 358 | .then(conversation => { 359 | document.getElementById('messageList').innerHTML = ''; 360 | document.getElementById('responseContainer').innerHTML = ''; 361 | 362 | conversation.messages.forEach((message) => { 363 | addMessageItem(message.text, message.role); 364 | }); 365 | 366 | if (conversation.responses && Array.isArray(conversation.responses)) { 367 | conversation.responses.forEach((response, index) => { 368 | displayRealTimeResponse({ 369 | response: response, 370 | requestId: index + 1 371 | }); // gpt_pilot_debugging_log 372 | }); 373 | } else { 374 | console.log('No responses found for this conversation.'); // gpt_pilot_debugging_log 375 | } 376 | 377 | console.log('Conversation details loaded and displayed.'); // gpt_pilot_debugging_log 378 | }).catch(error => { 379 | console.error('Failed to load conversation details:', error.message, error.stack); // gpt_pilot_debugging_log 380 | alert(`Failed to load conversation details: ${error.message}`); 381 | }); 382 | 383 | } 384 | 385 | let socket = null; 386 | }); -------------------------------------------------------------------------------- /public/app.js or other JS file responsible for UI events: -------------------------------------------------------------------------------- 1 | // Remove message item function moved to global scope 2 | function removeMessageItem(element) { 3 | try { 4 | const messageItem = element.closest('div'); 5 | messageItem.remove(); 6 | console.log('Message item removed successfully'); 7 | } catch (error) { 8 | console.error('Failed to remove message item:', error.message, error.stack); 9 | } 10 | } 11 | 12 | // Function to delete a response element 13 | // Modified deleteResponse function to include requestId in the API endpoint 14 | function deleteResponse(event, requestId, responseId) { 15 | event.preventDefault(); 16 | // Updated endpoint to include both requestId and responseId 17 | console.log(`Initiating DELETE request to URL: /api/delete-response/${requestId}/${responseId}`); // gpt_pilot_debugging_log 18 | fetch(`/api/delete-response/${requestId}/${responseId}`, { 19 | method: 'DELETE' 20 | }) 21 | .then(response => { 22 | if (!response.ok) { 23 | throw new Error(`HTTP error! Status: ${response.status}`); 24 | } 25 | // Receive confirmation from server that deletion was successful 26 | const responseElement = document.getElementById(`response-${requestId}`); 27 | if (responseElement) { 28 | // Remove the specified response element from the DOM 29 | responseElement.remove(); 30 | console.log(`Response #${requestId} deleted successfully`); 31 | } else { 32 | // Log error if the response element could not be located 33 | console.error(`Could not find response element #${requestId} in the DOM.`); 34 | } 35 | }) 36 | .catch(error => { 37 | console.error('Failed to delete response:', error.message, error.stack); 38 | alert(`Failed to delete response: ${error.message}`); 39 | }); 40 | } 41 | 42 | document.addEventListener('DOMContentLoaded', () => { 43 | // Add event listener for the SUBMIT button 44 | const submitButton = document.getElementById('submitButton'); 45 | if (submitButton) { 46 | submitButton.addEventListener('click', submitConversation); 47 | } else { 48 | console.error('Submit button not found'); 49 | } 50 | 51 | // Function to add a new message item to the list 52 | function addMessageItem(text = '', role = 'user') { 53 | const messageListElement = document.getElementById('messageList'); 54 | const newMessageItem = document.createElement('div'); 55 | newMessageItem.classList.add('flex', 'items-center', 'mb-2', 'bg-white', 'border', 'p-2', 'rounded', 'shadow'); 56 | newMessageItem.innerHTML = ` 57 | 58 | 63 | 64 | `; 65 | messageListElement.appendChild(newMessageItem); 66 | console.log('Added new message item to list'); 67 | } 68 | 69 | // Event listener for the Add Message button 70 | const addMessageButton = document.getElementById('addMessageButton'); 71 | if (addMessageButton) { 72 | addMessageButton.addEventListener('click', () => { 73 | try { 74 | addMessageItem(); 75 | console.log('New message item added.'); 76 | } catch (error) { 77 | console.error('Failed to add new message item:', error.message, error.stack); 78 | } 79 | }); 80 | } else { 81 | console.error('Add Message button not found'); 82 | } 83 | 84 | // Add initial message item on load 85 | addMessageItem(); 86 | 87 | // Function to load messages from JSON 88 | function loadMessagesFromJson() { 89 | try { 90 | const jsonTextarea = document.getElementById('jsonImportTextarea'); 91 | const messages = JSON.parse(jsonTextarea.value); 92 | const messageList = document.getElementById('messageList'); 93 | messageList.innerHTML = ''; // Clear existing messages 94 | 95 | messages.forEach(message => { 96 | addMessageItem(message.text, message.role); // Reuse function to add messages to the UI 97 | }); 98 | console.log('Messages loaded from JSON.'); // Logging successful operation 99 | } catch (error) { 100 | // Proper error handling and logging with stack trace 101 | console.error('Failed to load messages from JSON:', error.message, error.stack); 102 | // Inform the user through the UI that there was an error 103 | const jsonErrorElement = document.getElementById('jsonError'); 104 | if (jsonErrorElement) { 105 | jsonErrorElement.textContent = error.message; 106 | jsonErrorElement.classList.remove('hidden'); 107 | } else { 108 | // If the error display element doesn't exist, log the error in the console 109 | console.error('JSON error display element not found in the UI.'); 110 | } 111 | } 112 | } 113 | 114 | // Event listener for the Load Messages button 115 | const loadMessagesButton = document.getElementById('loadMessagesButton'); 116 | if (loadMessagesButton) { 117 | loadMessagesButton.addEventListener('click', loadMessagesFromJson); 118 | console.log('Event listener for Load Messages button added.'); // Logging successful operation 119 | } else { 120 | // Error handling with proper logging if the element is not found 121 | console.error('Load Messages button not found'); 122 | } 123 | 124 | // Function to handle OpenAI API errors 125 | function handleOpenAiError(error) { 126 | console.error('OpenAI Error:', error.message, error.stack); 127 | } 128 | 129 | // Function to display responses in real-time 130 | function displayRealTimeResponse(data) { 131 | const responseContainer = document.getElementById('responseContainer'); 132 | const { response, requestId } = data; 133 | const responseElement = document.createElement('details'); 134 | responseElement.id = `response-${requestId}`; 135 | 136 | const deleteButton = document.createElement('button'); 137 | deleteButton.classList.add('ml-2', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded'); 138 | deleteButton.textContent = 'Delete'; 139 | // Log added to track delete button event attachment 140 | console.log(`Attempting to delete. Request ID: ${requestId}, Response ID: ${response.id}`); // gpt_pilot_debugging_log 141 | deleteButton.addEventListener('click', (event) => deleteResponse(event, requestId, response.id)); // Make sure to adjust according to the actual data structure which should contain id or a suitable identifier for response. 142 | 143 | responseElement.innerHTML = ` 144 | Response #${requestId} 145 |

${response.choices[0].message.content}

146 | `; 147 | responseElement.appendChild(deleteButton); 148 | 149 | responseContainer.appendChild(responseElement); 150 | console.log(`Response #${requestId} displayed`); 151 | } 152 | 153 | // Function to collect messages and make a POST request on submit 154 | async function submitConversation() { 155 | try { 156 | const messageElements = document.querySelectorAll('#messageList > div'); 157 | const messages = Array.from(messageElements).map(messageElement => { 158 | const textArea = messageElement.querySelector('textarea'); 159 | const roleSelect = messageElement.querySelector('select'); 160 | return { 161 | text: textArea.value, 162 | role: roleSelect.value 163 | }; 164 | }); 165 | const apiRequestsCount = parseInt(document.getElementById('apiRequestsCount').value, 10) || 0; 166 | if (Number.isNaN(apiRequestsCount) || apiRequestsCount < 0 || apiRequestsCount > 100) { 167 | throw new Error('The number of API requests must be between 0 and 100.'); 168 | } 169 | if (socket === null) { 170 | console.log('Establishing WebSocket connection.'); 171 | socket = io(); 172 | socket.on('openai_real_response', displayRealTimeResponse); 173 | socket.on('openai_error', handleOpenAiError); 174 | } 175 | const response = await fetch('/submit', { 176 | method: 'POST', 177 | headers: { 178 | 'Content-Type': 'application/json', 179 | }, 180 | body: JSON.stringify({ messages, apiRequestsCount }), 181 | }); 182 | if (!response.ok) { 183 | throw new Error(`HTTP error! Status: ${response.status}`); 184 | } 185 | console.log('Conversation submitted successfully.'); 186 | } catch (error) { 187 | console.error('Failed to submit conversation:', error.message, error.stack); 188 | } 189 | } 190 | 191 | // Add event listener to the Save Conversation button 192 | const saveConversationButton = document.getElementById('saveConversationButton'); 193 | if (saveConversationButton) { 194 | saveConversationButton.addEventListener('click', saveConversation); 195 | } else { 196 | console.error('Save Conversation button not found'); 197 | } 198 | 199 | // Function to initiate the process of saving the current conversation state 200 | function saveConversation() { 201 | try { 202 | console.log('Initiating process to save the current conversation.'); 203 | // Collect message items 204 | const messageItems = document.querySelectorAll('#messageList > div'); 205 | const messages = Array.from(messageItems).map(item => { 206 | const textarea = item.querySelector('textarea'); 207 | const select = item.querySelector('select'); 208 | return { 209 | text: textarea.value, 210 | role: select.value 211 | }; 212 | }); 213 | 214 | // Collect OpenAI API responses from the UI 215 | const responseElements = document.querySelectorAll('#responseContainer > details'); 216 | const responses = Array.from(responseElements).map(detail => { 217 | const summary = detail.querySelector('summary'); 218 | const p = detail.querySelector('p'); 219 | return { 220 | text: p ? p.textContent : '', 221 | requestNumber: summary ? Number(summary.textContent.replace('Response #', '')) : null 222 | }; 223 | }); 224 | 225 | const conversationName = `Conversation_${new Date().toISOString()}`; 226 | const payload = { 227 | name: conversationName, 228 | messages: messages, 229 | responses: responses 230 | }; 231 | 232 | // Send the POST request to '/api/save-conversation' 233 | fetch('/api/save-conversation', { 234 | method: 'POST', 235 | headers: { 236 | 'Content-Type': 'application/json', 237 | }, 238 | body: JSON.stringify(payload), 239 | }) 240 | .then(response => { 241 | if (!response.ok) { 242 | throw new Error(`HTTP error! Status: ${response.status}`); 243 | } 244 | return response.json(); 245 | }) 246 | .then(data => { 247 | console.log('Conversation saved successfully:', data); 248 | alert('Conversation saved successfully!'); 249 | }) 250 | .catch(error => { 251 | console.error('Error saving conversation:', error.message, error.stack); 252 | alert(`Error saving conversation: ${error.message}`); 253 | }); 254 | } catch (error) { 255 | console.error('Failed to execute saveConversation function:', error.message, error.stack); 256 | alert(`Failed to save conversation: ${error.message}`); 257 | } 258 | } 259 | 260 | // Function to load and display a list of stored conversations 261 | function loadConversationsList() { 262 | console.log('Loading conversations list.'); // gpt_pilot_debugging_log 263 | fetch('/api/load-conversations', { 264 | method: 'GET', 265 | headers: { 266 | 'Content-Type': 'application/json', 267 | }, 268 | }) 269 | .then(response => { 270 | if (!response.ok) { 271 | throw new Error(`HTTP error! Status: ${response.status}`); 272 | } 273 | return response.json(); 274 | }) 275 | .then(conversations => { 276 | console.log('Conversations loaded:', conversations); // gpt_pilot_debugging_log 277 | const conversationsListContainer = document.getElementById('conversationsListContainer') || createConversationsListContainer(); 278 | conversationsListContainer.innerHTML = ''; // Clear previous conversation list 279 | 280 | conversations.forEach(conversation => { 281 | const listItem = document.createElement('button'); 282 | listItem.textContent = conversation.name; 283 | listItem.classList.add('block', 'text-left', 'p-2', 'hover:bg-gray-200', 'w-full', 'mt-1', 'relative'); 284 | 285 | // Delete button 286 | const deleteButton = document.createElement('button'); 287 | deleteButton.textContent = 'Delete'; 288 | deleteButton.classList.add('bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-1', 'px-2', 'rounded', 'absolute', 'right-0'); 289 | deleteButton.onclick = () => deleteConversation(conversation._id); 290 | listItem.appendChild(deleteButton); 291 | conversationsListContainer.appendChild(listItem); 292 | }); 293 | console.log('Conversations list loaded.'); 294 | }) 295 | .catch(error => { 296 | console.error('Failed to load conversations list:', error.message, error.stack); // gpt_pilot_debugging_log 297 | }); 298 | } 299 | 300 | // Function to delete a conversation 301 | function deleteConversation(conversationId) { 302 | fetch(`/api/delete-conversation/${conversationId}`, { 303 | method: 'DELETE' 304 | }) 305 | .then(response => { 306 | if (!response.ok) { 307 | throw new Error(`HTTP error! Status: ${response.status}`); 308 | } 309 | return response.json(); 310 | }) 311 | .then(data => { 312 | alert('Conversation deleted successfully.'); 313 | loadConversationsList(); // Refresh the list 314 | }) 315 | .catch(error => { 316 | console.error('Failed to delete conversation:', error.message, error.stack); 317 | alert(`Failed to delete conversation: ${error.message}`); 318 | }); 319 | } 320 | 321 | // Add event listener to Load Conversation button 322 | const loadConversationButton = document.getElementById('loadConversationButton'); 323 | if (loadConversationButton) { 324 | loadConversationButton.addEventListener('click', loadConversationsList); 325 | } else { 326 | console.error('Load Conversation button not found'); 327 | } 328 | 329 | // Function to load and display a specific conversation's details 330 | function loadConversationDetails(conversationId) { 331 | console.log(`Loading conversation details for ID: ${conversationId}`); // gpt_pilot_debugging_log 332 | fetch(`/api/load-conversations/${conversationId}`, { 333 | method: 'GET', 334 | headers: { 335 | 'Content-Type': 'application/json', 336 | }, 337 | }) 338 | .then(response => { 339 | if (!response.ok) { 340 | console.error(`HTTP error! Status: ${response.status}`); 341 | throw new Error(`HTTP error! Status: ${response.status}`); 342 | } 343 | return response.json(); 344 | }) 345 | .then(conversation => { 346 | // Clear existing messages and responses from the UI 347 | document.getElementById('messageList').innerHTML = ''; 348 | document.getElementById('responseContainer').innerHTML = ''; 349 | 350 | // Recreate message list items based on the loaded conversation 351 | conversation.messages.forEach((message) => { 352 | addMessageItem(message.text, message.role); 353 | }); 354 | 355 | // Check if the responses object exists before trying to iterate over it 356 | if (conversation.responses && Array.isArray(conversation.responses)) { 357 | // Recreate OpenAI API responses in the UI 358 | conversation.responses.forEach((response, index) => { 359 | const responseElement = document.createElement('details'); 360 | responseElement.innerHTML = ` 361 | Response #${index + 1} 362 |

${response.text}

363 | `; 364 | document.getElementById('responseContainer').appendChild(responseElement); 365 | }); 366 | } else { 367 | console.error('No responses found for this conversation.'); 368 | } 369 | 370 | console.log('Conversation details loaded and displayed.'); 371 | }) 372 | .catch(error => { 373 | console.error('Failed to load conversation details:', error.message, error.stack); 374 | alert(`Failed to load conversation details: ${error.message}`); 375 | }); 376 | } 377 | 378 | // Optional helper function to create the conversations list container if it doesn't exist 379 | function createConversationsListContainer() { 380 | const appElement = document.getElementById('app'); 381 | const newListContainer = document.createElement('div'); 382 | newListContainer.id = 'conversationsListContainer'; 383 | appElement.insertBefore(newListContainer, appElement.firstChild); // insert before the first child 384 | return newListContainer; 385 | } 386 | 387 | let socket = null; 388 | }); -------------------------------------------------------------------------------- /public/conversationHandler.js: -------------------------------------------------------------------------------- 1 | import { addMessageItem } from './messageItems.js'; 2 | 3 | export async function saveConversation() { 4 | const name = prompt('Enter a name for this conversation:').trim(); 5 | const textAreas = document.querySelectorAll('#messageList textarea'); 6 | const roles = document.querySelectorAll('#messageList select'); 7 | const messages = Array.from(textAreas, (textArea, index) => ({ 8 | text: textArea.value, 9 | role: roles[index].value 10 | })); 11 | // Include dummy responses to comply with expected data format 12 | const requestBody = { name, messages, responses: [] }; 13 | try { 14 | const response = await fetch('/api/save-conversation', { 15 | method: 'POST', 16 | headers: { 'Content-Type': 'application/json' }, 17 | body: JSON.stringify(requestBody) 18 | }); 19 | if (!response.ok) { 20 | const errorDetail = await response.text(); 21 | console.error(`Save Conversation failed: HTTP error! Status: ${response.status}, Details: ${errorDetail}`); // gpt_pilot_debugging_log 22 | throw new Error(`HTTP error! Status: ${response.status}, Details: ${errorDetail}`); 23 | } 24 | console.log('Conversation saved successfully.', await response.json()); // gpt_pilot_debugging_log 25 | alert('Conversation saved successfully.'); 26 | } catch (error) { 27 | console.error('Save Conversation failed:', error.message, error.stack); // gpt_pilot_debugging_log 28 | alert(`Save Conversation failed: ${error.message}`); 29 | } 30 | } 31 | 32 | export async function loadConversationsList() { 33 | console.log('Attempting to load conversations list.'); // gpt_pilot_debugging_log 34 | try { 35 | const response = await fetch('/api/load-conversations'); 36 | if (!response.ok) { 37 | console.error(`Loading conversations list failed: HTTP error! Status: ${response.status}`); // gpt_pilot_debugging_log 38 | throw new Error(`HTTP error! Status: ${response.status}`); 39 | } 40 | const conversations = await response.json(); 41 | console.log('Conversations loaded:', conversations); // gpt_pilot_debugging_log 42 | const listContainer = document.getElementById('conversationsListContainer'); 43 | listContainer.innerHTML = ''; // Clear previous conversations 44 | conversations.forEach(conversation => { 45 | const conversationItem = document.createElement('div'); 46 | conversationItem.textContent = conversation.name; 47 | conversationItem.classList.add('cursor-pointer', 'hover:bg-gray-200', 'p-2'); 48 | conversationItem.addEventListener('click', () => { 49 | console.log(`Loading conversation details for conversation with ID: ${conversation._id}`); // gpt_pilot_debugging_log 50 | loadConversation(conversation._id); 51 | }); 52 | listContainer.appendChild(conversationItem); 53 | }); 54 | console.log('Conversations list updated.'); // gpt_pilot_debugging_log 55 | } catch (error) { 56 | console.error('Failed to load conversations:', error.message, error.stack); // gpt_pilot_debugging_log 57 | alert(`Failed to load conversations: ${error.message}`); 58 | } 59 | } 60 | 61 | export async function loadConversation(conversationId) { 62 | console.log(`Attempting to load conversation with ID: ${conversationId}.`); // gpt_pilot_debugging_log 63 | try { 64 | const response = await fetch(`/api/load-conversations/${conversationId}`); 65 | if (!response.ok) { 66 | console.error(`Loading conversation failed: HTTP error! Status: ${response.status}`); // gpt_pilot_debugging_log 67 | throw new Error(`HTTP error! Status: ${response.status}`); 68 | } 69 | const conversation = await response.json(); 70 | console.log('Conversation loaded successfully:', conversation); // gpt_pilot_debugging_log 71 | displayLoadedConversation(conversation); 72 | } catch (error) { 73 | console.error('Failed to load conversation:', error.message, error.stack); // gpt_pilot_debugging_log 74 | alert(`Failed to load conversation: ${error.message}`); 75 | } 76 | } 77 | 78 | function displayLoadedConversation(conversation) { 79 | const messageListElement = document.getElementById('messageList'); 80 | messageListElement.innerHTML = ''; // Clear existing messages 81 | conversation.messages.forEach(message => { 82 | console.log(`Adding message: ${message.text} with role: ${message.role}`); // gpt_pilot_debugging_log 83 | addMessageItem(message.text, message.role); 84 | }); 85 | console.log(`Displayed loaded conversation: ${conversation.name}`); // gpt_pilot_debugging_log 86 | } -------------------------------------------------------------------------------- /public/displayRealTimeResponse.js: -------------------------------------------------------------------------------- 1 | import { deleteResponse } from './responseDeletionHandler.js'; 2 | 3 | function displayResponseElement(responseId, responseText, conversationId) { 4 | const responseContainer = document.getElementById('responseContainer'); 5 | if (!responseContainer) { 6 | console.error('Response container not found in the DOM'); // gpt_pilot_debugging_log 7 | return; 8 | } 9 | 10 | const responseElement = document.createElement('div'); 11 | responseElement.setAttribute('id', `response-${responseId}`); 12 | responseElement.classList.add('response-item', 'mb-4', 'p-4', 'bg-white', 'rounded-lg', 'shadow'); 13 | 14 | const textElement = document.createElement('p'); 15 | textElement.textContent = responseText; 16 | responseElement.appendChild(textElement); 17 | 18 | const deleteButton = document.createElement('button'); 19 | deleteButton.textContent = 'Delete'; 20 | deleteButton.classList.add('delete-response-btn', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded', 'ml-4'); 21 | deleteButton.addEventListener('click', () => { 22 | if (confirm('Are you sure you want to delete this response?')) { 23 | deleteResponse(conversationId, responseId).catch(error => { 24 | console.error('Failed to delete response:', error.message, error.stack); // gpt_pilot_debugging_log 25 | alert(`Failed to delete response: ${error.message}`); 26 | }); 27 | } 28 | }); 29 | responseElement.appendChild(deleteButton); 30 | 31 | responseContainer.appendChild(responseElement); 32 | 33 | console.log(`Response for ${responseId} displayed successfully.`); // gpt_pilot_debugging_log 34 | } 35 | 36 | export function handleRealTimeResponse(responseData) { 37 | console.log('Handling real-time response:', responseData); // gpt_pilot_debugging_log 38 | 39 | // Adding a log to print the structure of responseData as part of debugging 40 | console.log(`Received response structure: `, JSON.stringify(responseData, null, 2)); // gpt_pilot_debugging_log 41 | 42 | if (!responseData) { 43 | console.error('Response data is undefined or null.', responseData); // gpt_pilot_debugging_log 44 | return; 45 | } 46 | 47 | if (!responseData.response || !responseData.response.choices || responseData.response.choices.length === 0) { 48 | console.error('Received response is empty or improperly formatted', responseData); // gpt_pilot_debugging_log 49 | return; 50 | } 51 | 52 | if (!responseData.conversationId) { 53 | console.error('Conversation ID is missing from the response data', responseData); // gpt_pilot_debugging_log 54 | return; 55 | } 56 | 57 | 58 | const responseId = responseData.responseId || `response-${Date.now()}`; 59 | console.log(`Determined responseId: ${responseId}`); // gpt_pilot_debugging_log 60 | 61 | // Extracting the text from the OpenAI response correctly, 62 | // and providing a default 'No text returned' message if the responseText is empty 63 | const responseText = responseData.response.choices[0].text || 'No text returned'; 64 | console.log(`Generated responseId: ${responseId}, with text: ${responseText}`); // gpt_pilot_debugging_log 65 | 66 | displayResponseElement(responseId, responseText, responseData.conversationId); 67 | } -------------------------------------------------------------------------------- /public/displayResponses.js: -------------------------------------------------------------------------------- 1 | export function displayResponseInUI(data) { 2 | const responseContainer = document.getElementById('responseContainer'); 3 | if (!responseContainer) { 4 | console.error('Response container not found in the DOM'); 5 | return; 6 | } 7 | const responseElement = document.createElement('div'); 8 | responseElement.classList.add('response-item', 'mb-4', 'p-4', 'bg-white', 'rounded-lg', 'shadow'); 9 | responseElement.setAttribute('id', `response-${data.responseId}`); 10 | 11 | const responseText = document.createElement('p'); 12 | responseText.textContent = `Response: ${data.response.choices[0].text}`; 13 | 14 | const deleteButton = document.createElement('button'); 15 | deleteButton.textContent = 'Delete'; 16 | deleteButton.classList.add('delete-response-btn', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded', 'ml-auto'); 17 | deleteButton.onclick = function () { 18 | console.log(`Delete response button clicked for response ID: ${data.responseId}`); 19 | document.getElementById(`response-${data.responseId}`).remove(); 20 | }; 21 | 22 | responseElement.appendChild(responseText); 23 | responseElement.appendChild(deleteButton); 24 | responseContainer.appendChild(responseElement); 25 | } -------------------------------------------------------------------------------- /public/eventBindings.js: -------------------------------------------------------------------------------- 1 | import { submitConversation } from './submissionHandler.js'; 2 | import { saveConversation, loadConversationsList } from './conversationHandler.js'; 3 | 4 | function bindEvents() { 5 | const submitButton = document.getElementById('submitButton'); 6 | if (submitButton) { 7 | submitButton.addEventListener('click', (event) => { 8 | event.preventDefault(); 9 | try { 10 | submitConversation(); 11 | console.log('Submit button clicked.'); // gpt_pilot_debugging_log 12 | } catch (error) { 13 | console.error('Error when clicking Submit button:', error.message, error.stack); // gpt_pilot_debugging_log 14 | alert(`Error during submission: ${error.message}`); 15 | } 16 | }); 17 | } else { 18 | console.error('Submit button not found.'); // gpt_pilot_debugging_log 19 | } 20 | 21 | const saveConversationButton = document.getElementById('saveConversationButton'); 22 | if (saveConversationButton) { 23 | saveConversationButton.addEventListener('click', (event) => { 24 | event.preventDefault(); 25 | try { 26 | saveConversation(); 27 | console.log('Save Conversation button clicked.'); // gpt_pilot_debugging_log 28 | } catch (error) { 29 | console.error('Error when clicking Save Conversation button:', error.message, error.stack); // gpt_pilot_debugging_log 30 | alert(`Error during saving conversation: ${error.message}`); 31 | } 32 | }); 33 | } else { 34 | console.error('Save Conversation button not found.'); // gpt_pilot_debugging_log 35 | } 36 | 37 | const loadConversationButton = document.getElementById('loadConversationButton'); 38 | if (loadConversationButton) { 39 | loadConversationButton.addEventListener('click', (event) => { 40 | event.preventDefault(); 41 | try { 42 | loadConversationsList(); 43 | console.log('Load Conversation button clicked.'); // gpt_pilot_debugging_log 44 | } catch (error) { 45 | console.error('Failed to load conversations:', error.message, error.stack); // gpt_pilot_debugging_log 46 | alert(`Failed to load conversations: ${error.message}`); 47 | } 48 | }); 49 | } else { 50 | console.error('Load Conversation button not found.'); // gpt_pilot_debugging_log 51 | } 52 | } 53 | 54 | export { bindEvents }; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | This is a binary file and cannot be displayed inline. -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Prompt Engineering 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 | 24 | 25 |
26 | 27 |
28 | 29 |
30 | 33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /public/js/bindEvents.js: -------------------------------------------------------------------------------- 1 | import { loadConversation } from './conversationHandler.js'; 2 | 3 | export function bindConversationEvents() { 4 | document.querySelectorAll('.conversation-item').forEach(item => { 5 | item.addEventListener('click', () => { 6 | loadConversation(item.dataset.conversationId); 7 | }); 8 | }); 9 | console.log('Event bindings for conversation items established.'); 10 | } -------------------------------------------------------------------------------- /public/js/conversationDetailsHandler.js: -------------------------------------------------------------------------------- 1 | export function bindConversationSelection() { 2 | const conversationItems = document.querySelectorAll('.conversation-item'); 3 | conversationItems.forEach(item => { 4 | item.addEventListener('click', () => loadConversationDetails(item.dataset.conversationId)); 5 | }); 6 | } 7 | 8 | async function loadConversationDetails(conversationId) { 9 | console.log(`Attempting to load conversation details for ID: ${conversationId}.`); // gpt_pilot_debugging_log 10 | try { 11 | const response = await fetch(`/api/load-conversations/${conversationId}`); 12 | if (!response.ok) { 13 | throw new Error(`HTTP status: ${response.status}`); 14 | } 15 | const conversation = await response.json(); 16 | displayLoadedConversation(conversation); 17 | } catch (error) { 18 | console.error(`Error loading conversation details: ${error.message}`, error.stack); // gpt_pilot_debugging_log 19 | alert(`Failed to load conversation details: ${error.message}`); 20 | } 21 | } 22 | 23 | function displayLoadedConversation(conversation) { 24 | console.log(`Displayed loaded conversation: ${conversation.name}`); // gpt_pilot_debugging_log 25 | // The specific UI manipulation logic goes here... 26 | } 27 | -------------------------------------------------------------------------------- /public/js/conversationHandler.js: -------------------------------------------------------------------------------- 1 | import { addMessageItem } from './messageItems.js'; 2 | import { bindConversationEvents } from './bindEvents.js'; 3 | 4 | export async function saveConversation() { 5 | const name = prompt('Enter a name for this conversation:').trim(); 6 | const textAreas = document.querySelectorAll('#messageList textarea'); 7 | const roles = document.querySelectorAll('#messageList select'); 8 | const messages = Array.from(textAreas, (textArea, index) => ({ 9 | text: textArea.value, 10 | role: roles[index].value 11 | })); 12 | // Include dummy responses to comply with expected data format 13 | const requestBody = { name, messages, responses: [] }; 14 | try { 15 | const response = await fetch('/api/save-conversation', { 16 | method: 'POST', 17 | headers: { 'Content-Type': 'application/json' }, 18 | body: JSON.stringify(requestBody) 19 | }); 20 | if (!response.ok) { 21 | const errorDetail = await response.text(); 22 | console.error(`Save Conversation failed: HTTP error! Status: ${response.status}, Details: ${errorDetail}`); // gpt_pilot_debugging_log 23 | throw new Error(`HTTP error! Status: ${response.status}, Details: ${errorDetail}`); 24 | } 25 | console.log('Conversation saved successfully.', await response.json()); // gpt_pilot_debugging_log 26 | alert('Conversation saved successfully.'); 27 | } catch (error) { 28 | console.error('Save Conversation failed:', error.message, error.stack); // gpt_pilot_debugging_log 29 | alert(`Save Conversation failed: ${error.message}`); 30 | } 31 | } 32 | 33 | export async function loadConversationsList() { 34 | console.log('Attempting to load conversations list.'); // gpt_pilot_debugging_log 35 | try { 36 | const response = await fetch('/api/load-conversations'); 37 | if (!response.ok) { 38 | console.error(`Loading conversations list failed: HTTP error! Status: ${response.status}`); // gpt_pilot_debugging_log 39 | throw new Error(`HTTP error! Status: ${response.status}`); 40 | } 41 | const conversations = await response.json(); 42 | console.log('Conversations loaded:', conversations); // gpt_pilot_debugging_log 43 | const listContainer = document.getElementById('conversationsListContainer'); 44 | listContainer.innerHTML = ''; // Clear previous conversations 45 | conversations.forEach(conversation => { 46 | const conversationItem = document.createElement('div'); 47 | conversationItem.textContent = conversation.name; 48 | conversationItem.classList.add('cursor-pointer', 'hover:bg-gray-200', 'p-2'); 49 | conversationItem.dataset.conversationId = conversation._id; // Store conversation ID in dataset for retrieval 50 | conversationItem.addEventListener('click', () => { 51 | console.log(`Loading conversation details for conversation with ID: ${conversation._id}`); // gpt_pilot_debugging_log 52 | loadConversation(conversation._id); 53 | }); 54 | listContainer.appendChild(conversationItem); 55 | }); 56 | console.log('Conversations list updated.'); // gpt_pilot_debugging_log 57 | bindConversationEvents(); // Ensure event listeners are set up for loaded conversation items 58 | } catch (error) { 59 | console.error('Failed to load conversations:', error.message, error.stack); // gpt_pilot_debugging_log 60 | alert(`Failed to load conversations: ${error.message}`); 61 | } 62 | } 63 | 64 | export async function loadConversation(conversationId) { 65 | console.log(`Attempting to load conversation with ID: ${conversationId}.`); // gpt_pilot_debugging_log 66 | try { 67 | const response = await fetch(`/api/load-conversations/${conversationId}`); 68 | if (!response.ok) { 69 | console.error(`Loading conversation failed: HTTP error! Status: ${response.status}`); // gpt_pilot_debugging_log 70 | throw new Error(`HTTP error! Status: ${response.status}`); 71 | } 72 | const conversation = await response.json(); 73 | console.log('Conversation loaded successfully:', conversation); // gpt_pilot_debugging_log 74 | displayLoadedConversation(conversation); 75 | } catch (error) { 76 | console.error('Failed to load conversation:', error.message, error.stack); // gpt_pilot_debugging_log 77 | alert(`Failed to load conversation: ${error.message}`); 78 | } 79 | } 80 | 81 | function displayLoadedConversation(conversation) { 82 | const messageListElement = document.getElementById('messageList'); 83 | messageListElement.innerHTML = ''; // Clear existing messages 84 | conversation.messages.forEach(message => { 85 | console.log(`Adding message: ${message.text} with role: ${message.role}`); // gpt_pilot_debugging_log 86 | addMessageItem(message.text, message.role); 87 | }); 88 | console.log(`Displayed loaded conversation: ${conversation.name}`); // gpt_pilot_debugging_log 89 | } 90 | 91 | function bindConversationEvents() { 92 | console.log('Binding event listeners for conversation items.'); // gpt_pilot_debugging_log 93 | document.querySelectorAll('.cursor-pointer').forEach(item => { 94 | item.addEventListener('click', () => { 95 | const conversationId = item.dataset.conversationId; 96 | console.log(`Conversation item clicked: Loading conversation with ID: ${conversationId}`); // gpt_pilot_debugging_log 97 | loadConversation(conversationId); 98 | }); 99 | }); 100 | } -------------------------------------------------------------------------------- /public/js/displayRealTimeResponse.js: -------------------------------------------------------------------------------- 1 | import { deleteResponse } from './responseDeletionHandler.js'; 2 | 3 | export function handleRealTimeResponse(responseData) { 4 | console.log('Received real-time response data:', JSON.stringify(responseData, null, 2)); // gpt_pilot_debugging_log 5 | console.log('Handling real-time response:', responseData); // gpt_pilot_debugging_log 6 | 7 | if (!responseData || !responseData.response || !responseData.response.choices || responseData.response.choices.length === 0) { 8 | console.error('Invalid or empty response data.', responseData); // gpt_pilot_debugging_log 9 | return; 10 | } 11 | 12 | // Added check for 'system' message role for debugging purposes 13 | if (responseData.role === 'system') { 14 | console.log('Displaying system message in UI', responseData); // gpt_pilot_debugging_log 15 | } 16 | 17 | const responseId = responseData.responseId || `response-${Date.now()}`; 18 | const responseText = responseData.response.choices[0].text; // Assuming the correct property is text 19 | console.log(`Generated responseId: ${responseId}, with text: ${responseText}`); // gpt_pilot_debugging_log 20 | 21 | displayResponseElement(responseId, responseText, responseData.conversationId); 22 | } 23 | 24 | function displayResponseElement(responseId, responseText, conversationId) { 25 | console.log(`Attempting to display response element. Conversation ID: ${conversationId}, Response ID: ${responseId}`); // gpt_pilot_debugging_log 26 | const responseContainer = document.getElementById('responseContainer'); 27 | if (!responseContainer) { 28 | console.error('Response container not found in the DOM'); // gpt_pilot_debugging_log 29 | return; 30 | } 31 | 32 | const responseElement = document.createElement('div'); 33 | responseElement.setAttribute('id', `response-${responseId}`); 34 | responseElement.classList.add('response-item', 'mb-4', 'p-4', 'bg-white', 'rounded-lg', 'shadow'); 35 | 36 | const textElement = document.createElement('p'); 37 | textElement.textContent = responseText; 38 | responseElement.appendChild(textElement); 39 | 40 | const deleteButton = document.createElement('button'); 41 | deleteButton.textContent = 'Delete'; 42 | deleteButton.className = 'delete-response-btn bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded ml-4'; 43 | 44 | deleteButton.onclick = function() { 45 | console.log(`Delete button clicked for Conversation ID: ${conversationId}, Response ID: ${responseId}.`); // gpt_pilot_debugging_log 46 | deleteResponse(conversationId, responseId).then(() => { 47 | console.log(`Response with ID ${responseId} was successfully deleted.`); // gpt_pilot_debugging_log 48 | document.getElementById(`response-${responseId}`).remove(); 49 | }).catch(error => { 50 | console.error(`Failed to delete the response with ID: ${responseId}: ${error.message}`, error.stack); // gpt_pilot_debugging_log 51 | alert(`Error deleting response: ${error.message}`); 52 | }); 53 | }; 54 | 55 | responseElement.appendChild(deleteButton); 56 | 57 | responseContainer.appendChild(responseElement); 58 | 59 | console.log(`Response with ID: ${responseId} displayed successfully.`); // gpt_pilot_debugging_log 60 | } -------------------------------------------------------------------------------- /public/js/eventBindings.js: -------------------------------------------------------------------------------- 1 | import { submitConversation } from './submissionHandler.js'; 2 | import { addMessageItem } from './messageItemsHandler.js'; 3 | 4 | export function bindEvents() { 5 | console.log('Binding events.'); // gpt_pilot_debugging_log 6 | // Binding events logic, originally spread across app.js, should go here. 7 | } -------------------------------------------------------------------------------- /public/js/eventHandlers.js: -------------------------------------------------------------------------------- 1 | export function setupEventHandlers() { 2 | document.addEventListener('click', (event) => { 3 | if (event.target.classList.contains('delete-btn')) { 4 | const element = event.target; 5 | removeMessageItem(element); 6 | console.log('Delete button clicked and handler executed successfully.'); 7 | } 8 | }); 9 | console.log('Event handlers set up successfully.'); 10 | } -------------------------------------------------------------------------------- /public/js/messageItems.js: -------------------------------------------------------------------------------- 1 | export function addMessageItem(text = '', role = 'user') { 2 | try { 3 | const messageListElement = document.getElementById('messageList'); 4 | 5 | if (!messageListElement) { 6 | console.error('Message list element not found'); // gpt_pilot_debugging_log 7 | throw new Error('Message list element not found'); 8 | } 9 | 10 | // Generate unique identifier for each message item 11 | const uniqueId = `message-item-${Date.now()}`; 12 | 13 | console.log(`Adding a new message item. ID: ${uniqueId}, Text: ${text}, Role: ${role}`); // gpt_pilot_debugging_log 14 | 15 | const newMessageItem = document.createElement('div'); 16 | newMessageItem.id = uniqueId; 17 | newMessageItem.classList.add('flex', 'items-center', 'mb-2', 'bg-white', 'border', 'p-2', 'rounded', 'shadow'); 18 | 19 | // Ensure the switch or if-else handling roles correctly sets the dropdown. 20 | newMessageItem.innerHTML = ` 21 | 22 | 27 | `; 28 | 29 | const deleteButton = document.createElement('button'); 30 | deleteButton.textContent = 'X'; 31 | deleteButton.classList.add('delete-btn', 'ml-2', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded'); 32 | deleteButton.addEventListener('click', () => removeMessageItem(deleteButton, newMessageItem.id)); 33 | 34 | newMessageItem.appendChild(deleteButton); 35 | messageListElement.appendChild(newMessageItem); 36 | 37 | console.log(`Dropdown updated to include 'system' role.`); // gpt_pilot_debugging_log 38 | console.log(`Added UI for role: ${role}, with text: ${text}`); // gpt_pilot_debugging_log 39 | } catch (error) { 40 | console.error('Failed to add message item:', error.message, error.stack); // gpt_pilot_debugging_log 41 | } 42 | } 43 | 44 | function removeMessageItem(element, messageId) { 45 | try { 46 | const messageItem = element.closest('div'); 47 | if (messageItem && messageId) { 48 | messageItem.parentElement.removeChild(messageItem); 49 | console.log(`Message item with id ${messageId} removed successfully`); // gpt_pilot_debugging_log 50 | } else { 51 | console.error('Message item to remove not found or messageId is missing'); // gpt_pilot_debugging_log 52 | throw new Error('Message item to remove not found or messageId is missing'); 53 | } 54 | } catch (error) { 55 | console.error('Failed to remove message item:', error.message, error.stack); // gpt_pilot_debugging_log 56 | } 57 | } 58 | 59 | export { removeMessageItem }; -------------------------------------------------------------------------------- /public/js/messageItemsHandler.js: -------------------------------------------------------------------------------- 1 | // This file manages adding and removing message items to/from the conversation. 2 | export function addMessageItem(text = '', role = 'user') { 3 | console.log(`Adding message item. Text: ${text}, Role: ${role}`); // gpt_pilot_debugging_log 4 | // Original addMessageItem logic goes here. 5 | } 6 | 7 | export function removeMessageItem(element) { 8 | console.log('Removing message item.'); // gpt_pilot_debugging_log 9 | // Original removeMessageItem logic goes here. 10 | } -------------------------------------------------------------------------------- /public/js/responseDeletionHandler.js: -------------------------------------------------------------------------------- 1 | export function deleteResponse(conversationId, responseId) { 2 | return new Promise((resolve, reject) => { 3 | console.log(`Attempting to delete response. Conversation ID: ${conversationId}, Response ID: ${responseId}`); // gpt_pilot_debugging_log 4 | console.log(`Attempting to delete response with ID: ${responseId} from conversation: ${conversationId}.`); // gpt_pilot_debugging_log 5 | fetch(`/api/delete-response/${conversationId}/${responseId}`, { 6 | method: 'DELETE' 7 | }) 8 | .then(response => { 9 | if (!response.ok) { 10 | console.error(`Delete response failed with status: ${response.status}`, { conversationId, responseId }); // gpt_pilot_debugging_log 11 | throw new Error(`HTTP error! Status: ${response.status}`); 12 | } 13 | return response.json(); 14 | }) 15 | .then(data => { 16 | console.log(`Response with ID: ${responseId} in conversation: ${conversationId}, deleted successfully.`, data); // gpt_pilot_debugging_log 17 | resolve(); 18 | }) 19 | .catch(error => { 20 | console.error('Failed to delete response:', error.message, error.stack, { conversationId, responseId }); // gpt_pilot_debugging_log 21 | reject(error); 22 | }); 23 | }); 24 | } -------------------------------------------------------------------------------- /public/js/submissionHandler.js: -------------------------------------------------------------------------------- 1 | // This file will handle the submission of conversations. 2 | export function submitConversation() { 3 | console.log('submitConversation initialized.'); // gpt_pilot_debugging_log 4 | // Original submission logic goes here. 5 | // Placeholder logic for demonstration 6 | console.log('Conversation submitted.'); 7 | } -------------------------------------------------------------------------------- /public/messageItems.js: -------------------------------------------------------------------------------- 1 | export function addMessageItem(text = '', role = 'user') { 2 | try { 3 | const messageListElement = document.getElementById('messageList'); 4 | 5 | if (!messageListElement) { 6 | console.error('Message list element not found'); // gpt_pilot_debugging_log 7 | throw new Error('Message list element not found'); 8 | } 9 | 10 | // Generate unique identifier for each message item 11 | const uniqueId = `message-item-${Date.now()}`; 12 | 13 | console.log(`Adding a new message item. ID: ${uniqueId}, Text: ${text}, Role: ${role}`); // gpt_pilot_debugging_log 14 | 15 | const newMessageItem = document.createElement('div'); 16 | newMessageItem.id = uniqueId; 17 | newMessageItem.classList.add('flex', 'items-center', 'mb-2', 'bg-white', 'border', 'p-2', 'rounded', 'shadow'); 18 | 19 | // Ensure the switch or if-else handling roles correctly sets the dropdown. 20 | const roleOptions = { 21 | user: role === 'user' ? 'selected' : '', 22 | assistant: role === 'assistant' ? 'selected' : '', 23 | system: role === 'system' ? 'selected' : '' 24 | }; 25 | 26 | if (!['user', 'assistant', 'system'].includes(role)) { 27 | console.error(`Invalid role: ${role}`); // gpt_pilot_debugging_log 28 | throw new Error(`Invalid role: ${role}`); 29 | } 30 | 31 | newMessageItem.innerHTML += ` 32 | 33 | 38 | `; 39 | 40 | const deleteButton = document.createElement('button'); 41 | deleteButton.textContent = 'X'; 42 | deleteButton.classList.add('delete-btn', 'ml-2', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded'); 43 | deleteButton.addEventListener('click', () => removeMessageItem(deleteButton, newMessageItem.id)); 44 | 45 | newMessageItem.appendChild(deleteButton); 46 | messageListElement.appendChild(newMessageItem); 47 | 48 | console.log('New message item added successfully.'); // gpt_pilot_debugging_log 49 | } catch (error) { 50 | console.error('Failed to add message item:', error.message, error.stack); // gpt_pilot_debugging_log 51 | } 52 | } 53 | 54 | function removeMessageItem(element, messageId) { 55 | try { 56 | const messageItem = element.closest('div'); 57 | if (messageItem && messageId) { 58 | messageItem.parentElement.removeChild(messageItem); 59 | console.log(`Message item with id ${messageId} removed successfully`); // gpt_pilot_debugging_log 60 | } else { 61 | console.error('Message item to remove not found or messageId is missing'); // gpt_pilot_debugging_log 62 | throw new Error('Message item to remove not found or messageId is missing'); 63 | } 64 | } catch (error) { 65 | console.error('Failed to remove message item:', error.message, error.stack); // gpt_pilot_debugging_log 66 | } 67 | } 68 | 69 | export { removeMessageItem }; -------------------------------------------------------------------------------- /public/openAiRequestHandler.js: -------------------------------------------------------------------------------- 1 | export function initiateOpenAiRequest(message) { 2 | console.log('Initiating request to OpenAI with message:', message); 3 | fetch('/submit', { 4 | method: 'POST', 5 | headers: { 6 | 'Content-Type': 'application/json' 7 | }, 8 | body: JSON.stringify({ messages: [{ text: message, role: 'user' }], apiRequestsCount: 1 }) 9 | }) 10 | .then(response => response.json()) 11 | .then(data => { 12 | console.log('Received response from OpenAI:', data); 13 | displayResponseInUI(data); 14 | }) 15 | .catch(error => console.error('Error fetching response from OpenAI:', error)); 16 | } 17 | 18 | function displayResponseInUI(data) { 19 | const responseContainer = document.getElementById('responseContainer'); 20 | if (!responseContainer) { 21 | console.error('Response container not found'); 22 | return; 23 | } 24 | responseContainer.innerHTML = ''; 25 | data.forEach((responseData, index) => { 26 | const responseElement = document.createElement('div'); 27 | responseElement.classList.add('response-item', 'p-4', 'bg-gray-100', 'rounded', 'mt-2'); 28 | responseElement.textContent = `Response ${index + 1}: ${responseData.response.choices[0].text}`; 29 | responseContainer.appendChild(responseElement); 30 | }); 31 | } -------------------------------------------------------------------------------- /public/responseDeletionHandler.js: -------------------------------------------------------------------------------- 1 | function deleteResponse(conversationId, responseId) { 2 | console.log(`Attempting to delete response with ID: ${responseId} from conversation: ${conversationId}.`); // gpt_pilot_debugging_log 3 | if (!conversationId || !responseId) { 4 | console.error('Missing conversationId or responseId for deletion', { conversationId, responseId }); // gpt_pilot_debugging_log 5 | alert('Missing conversation ID or response ID for deletion.'); 6 | return; 7 | } 8 | 9 | fetch(`/api/delete-response/${conversationId}/${responseId}`, { 10 | method: 'DELETE' 11 | }).then(response => { 12 | if (!response.ok) { 13 | console.error(`Failed to delete the response with ID: ${responseId}. HTTP Status: ${response.status}`); // gpt_pilot_debugging_log 14 | throw new Error(`HTTP error! Status: ${response.status}`); 15 | } 16 | console.log(`Response with ID: ${responseId} in conversation: ${conversationId}, deleted successfully.`); // gpt_pilot_debugging_log 17 | const responseElement = document.getElementById(`response-${responseId}`); 18 | if (responseElement) { 19 | responseElement.remove(); 20 | } else { 21 | console.error(`Failed to find response element with ID: ${responseId} in DOM to remove.`); // gpt_pilot_debugging_log 22 | } 23 | }).catch(error => { 24 | console.error('Failed to delete response:', error.message, error.stack); // gpt_pilot_debugging_log 25 | alert(`Failed to delete response: ${error.message}`); 26 | }); 27 | } 28 | 29 | export { deleteResponse }; -------------------------------------------------------------------------------- /public/responseDisplayHandler.js: -------------------------------------------------------------------------------- 1 | export function handleRealTimeResponse(data) { 2 | console.log('Handling real-time response', data); // gpt_pilot_debugging_log 3 | if (!data || !data.response || !data.response.choices || data.response.choices.length <= 0) { 4 | console.error('Invalid response structure received.', data); // gpt_pilot_debugging_log 5 | return; 6 | } 7 | 8 | const responseId = data.responseId || `response-${Date.now()}`; 9 | const responseText = data.response.choices[0].text; 10 | 11 | displayResponseInUI(responseId, responseText, data.conversationId); 12 | } 13 | 14 | function displayResponseInUI(responseId, responseText, conversationId) { 15 | const responseContainer = document.getElementById('responseContainer'); 16 | if (!responseContainer) { 17 | console.error('Response container not found', { error: 'DOM element with id \"responseContainer\" not found.' }); // gpt_pilot_debugging_log 18 | return; 19 | } 20 | 21 | const responseElement = document.createElement('div'); 22 | responseElement.classList.add('response-item', 'mb-4', 'p-4', 'bg-white', 'rounded-lg', 'shadow'); 23 | responseElement.setAttribute('id', responseId); 24 | 25 | const textElement = document.createElement('p'); 26 | textElement.textContent = responseText; 27 | 28 | const deleteButton = document.createElement('button'); 29 | deleteButton.textContent = 'Delete'; 30 | deleteButton.classList.add('ml-4', 'bg-red-500', 'text-white', 'p-2', 'rounded'); 31 | deleteButton.onclick = () => document.getElementById(responseId).remove(); 32 | 33 | responseElement.appendChild(textElement); 34 | responseElement.appendChild(deleteButton); 35 | 36 | responseContainer.appendChild(responseElement); 37 | 38 | console.log(`Displayed response for ${responseId} successfully.`); // gpt_pilot_debugging_log 39 | } -------------------------------------------------------------------------------- /public/responseHandler.js: -------------------------------------------------------------------------------- 1 | export function displayResponseInUI(data) { 2 | // Adding initial data logging for debugging purposes 3 | console.log('Displaying response in UI:', data); // gpt_pilot_debugging_log 4 | 5 | const responseContainer = document.getElementById('responseContainer'); 6 | if (!responseContainer) { 7 | console.error('Response container not found in the DOM', { error: 'Dom element with id responseContainer not found.' }); // gpt_pilot_debugging_log 8 | return; 9 | } 10 | 11 | // Validate the structure of the data received, adding logging for debugging 12 | console.log(`Response data to be displayed: `, data); // gpt_pilot_debugging_log 13 | if (!data || !data.response || !data.response.choices || data.response.choices.length === 0) { 14 | console.error(`Invalid response data structure or empty response:`, data); // gpt_pilot_debugging_log 15 | return; 16 | } 17 | 18 | const responseElement = document.createElement('div'); 19 | responseElement.classList.add('response-item', 'mb-4', 'p-4', 'bg-white', 'rounded-lg', 'shadow'); 20 | responseElement.setAttribute('id', `response-${data.responseId}`); 21 | 22 | const responseText = document.createElement('p'); 23 | responseText.textContent = `Response: ${data.response.choices[0].text}`; 24 | responseElement.appendChild(responseText); 25 | 26 | const deleteButton = document.createElement('button'); 27 | deleteButton.textContent = 'Delete'; 28 | deleteButton.classList.add('delete-response-btn', 'bg-red-500', 'hover:bg-red-700', 'text-white', 'font-bold', 'py-2', 'px-4', 'rounded', 'ml-auto'); 29 | deleteButton.onclick = function () { 30 | try { 31 | const elementToRemove = document.getElementById(`response-${data.responseId}`); 32 | if (elementToRemove) { 33 | elementToRemove.remove(); 34 | console.log(`Response with ID: ${data.responseId} deleted successfully.`); // gpt_pilot_debugging_log 35 | } else { 36 | console.warn(`Unable to find element with ID: ${data.responseId} for deletion.`); // gpt_pilot_debugging_log 37 | } 38 | } catch (error) { 39 | console.error(`Error deleting response with ID: ${data.responseId}:`, error.message, error.stack); // gpt_pilot_debugging_log 40 | } 41 | }; 42 | responseElement.appendChild(deleteButton); 43 | 44 | responseContainer.appendChild(responseElement); 45 | 46 | console.log(`Response for ${data.responseId} displayed successfully.`); // gpt_pilot_debugging_log 47 | } 48 | 49 | export function clearResponseContainer() { 50 | console.log('Attempting to clear response container'); // gpt_pilot_debugging_log 51 | const responseContainer = document.getElementById('responseContainer'); 52 | if (responseContainer) { 53 | responseContainer.innerHTML = ''; 54 | console.log('Response container cleared successfully.'); // gpt_pilot_debugging_log 55 | } else { 56 | console.error('Failed to clear response container: Response container not found.'); // gpt_pilot_debugging_log 57 | } 58 | } -------------------------------------------------------------------------------- /public/submissionDisplayHandler.js: -------------------------------------------------------------------------------- 1 | export function displaySubmissionData(data) { 2 | const container = document.getElementById('responseContainer'); 3 | if (!container) { 4 | console.error('Response container not found in the DOM'); 5 | return; 6 | } 7 | container.innerHTML = ''; 8 | const submissionDetails = document.createElement('pre'); 9 | submissionDetails.textContent = JSON.stringify(data, null, 2); 10 | submissionDetails.classList.add('p-4', 'bg-gray-200', 'rounded', 'text-sm'); 11 | container.appendChild(submissionDetails); 12 | console.log('Displayed submission data successfully.'); 13 | } -------------------------------------------------------------------------------- /public/submissionHandler.js: -------------------------------------------------------------------------------- 1 | import { clearResponseContainer } from './responseHandler.js'; 2 | 3 | function submitConversation() { 4 | console.log('Preparing to submit conversation.'); // gpt_pilot_debugging_log 5 | clearResponseContainer(); // Clear responses from previous submissions 6 | console.log('Cleared previous responses.'); // gpt_pilot_debugging_log 7 | 8 | const textAreas = document.querySelectorAll('#messageList textarea'); 9 | const roles = document.querySelectorAll('#messageList select'); 10 | const messages = Array.from(textAreas).map((textArea, index) => ({ 11 | text: textArea.value, 12 | role: roles[index].value 13 | })); 14 | const apiRequestsCount = parseInt(document.getElementById('apiRequestsCount').value, 10); 15 | if (!messages.length || isNaN(apiRequestsCount)) { 16 | console.error('Invalid messages or apiRequestsCount'); // gpt_pilot_debugging_log 17 | return; 18 | } 19 | const requestBody = { 20 | messages, 21 | apiRequestsCount 22 | }; 23 | 24 | console.log('Submitting conversation with the following data:', requestBody); // gpt_pilot_debugging_log 25 | 26 | fetch('/submit', { 27 | method: 'POST', 28 | headers: { 29 | 'Content-Type': 'application/json' 30 | }, 31 | body: JSON.stringify(requestBody) 32 | }) 33 | .then(async response => { 34 | if (!response.ok) { 35 | const errorDetail = await response.text(); 36 | console.error(`Submit Conversation failed: HTTP error! Status: ${response.status}, Details: ${errorDetail}`); // gpt_pilot_debugging_log 37 | alert(`Submission error: ${errorDetail}`); 38 | return; 39 | } 40 | console.log('Conversation submitted successfully, waiting for responses.'); // gpt_pilot_debugging_log 41 | }) 42 | .catch(error => { 43 | console.error('Submit Conversation failed:', error.message, error.stack); // gpt_pilot_debugging_log 44 | }); 45 | } 46 | 47 | function saveConversation() { 48 | console.log('saveConversation function triggered.'); // gpt_pilot_debugging_log 49 | alert('Save Conversation is not yet implemented.'); 50 | } 51 | 52 | function loadConversationsList() { 53 | console.log('loadConversationsList function triggered.'); // gpt_pilot_debugging_log 54 | alert('Load Conversations is not yet implemented.'); 55 | } 56 | 57 | export { submitConversation, saveConversation, loadConversationsList }; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const path = require('path'); 4 | const { callOpenAiApi } = require('./openAiService'); 5 | const http = require('http'); 6 | const { Server } = require('socket.io'); 7 | const connectDB = require('./db'); 8 | const Conversation = require('./models/conversation'); 9 | const mongoose = require('mongoose'); // Ensure mongoose is required 10 | 11 | connectDB().catch(err => { 12 | console.error('Failed to connect to MongoDB:', err.stack); 13 | process.exit(1); 14 | }); 15 | 16 | const app = express(); 17 | 18 | // CORS Middleware to allow different HTTP methods 19 | app.use((req, res, next) => { 20 | res.header('Access-Control-Allow-Origin', '*'); 21 | res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS'); 22 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); 23 | next(); 24 | }); 25 | 26 | app.use(express.json()); 27 | app.use(express.static(path.join(__dirname, 'public'))); 28 | 29 | const server = http.createServer(app); 30 | const io = new Server(server); 31 | 32 | io.on('connection', (socket) => { 33 | console.log('A client connected: ' + socket.id); 34 | socket.on('disconnect', () => { 35 | console.log('Client disconnected: ' + socket.id); 36 | }); 37 | }); 38 | 39 | app.post('/submit', async (req, res) => { 40 | const { messages, apiRequestsCount } = req.body; 41 | console.log(`Submission received with ${apiRequestsCount} requests.`); 42 | 43 | if (!Array.isArray(messages) || typeof apiRequestsCount !== 'number' || apiRequestsCount < 0 || apiRequestsCount > 100) { 44 | console.error('Invalid input data or the number of API requests is out of bounds'); 45 | return res.status(400).json({ error: 'Invalid input data or the number of API requests is out of bounds' }); 46 | } 47 | 48 | const promises = []; 49 | const responses = []; 50 | 51 | for (let i = 0; i < apiRequestsCount; i++) { 52 | console.log(`Initiating OpenAI API call #${i + 1}`); // gpt_pilot_debugging_log 53 | promises.push(callOpenAiApi(messages, io, i + 1)); 54 | } 55 | 56 | res.status(202).send('Accepted'); 57 | 58 | try { 59 | await Promise.all(promises); 60 | console.log(`All ${apiRequestsCount} API calls have been processed`); 61 | 62 | const newConversation = new Conversation({ 63 | name: `Conversation_${new Date().toISOString()}`, 64 | messages, 65 | responses 66 | }); 67 | 68 | await newConversation.save(); 69 | console.log('Conversation saved:', newConversation._id); 70 | } catch (error) { 71 | console.error(`Error during parallel API requests:`, error.message, error.stack); // gpt_pilot_debugging_log 72 | io.emit('openai_error', { error: error.message, stack: error.stack }); 73 | } 74 | }); 75 | 76 | app.post('/api/save-conversation', async (req, res) => { 77 | try { 78 | const { name, messages, responses } = req.body; 79 | if (typeof name !== 'string' || !Array.isArray(messages)) { 80 | console.error('Invalid conversation data format', { name, messages, responses }); // gpt_pilot_debugging_log 81 | return res.status(400).send('Invalid conversation data format.'); 82 | } 83 | if (!Array.isArray(responses) || responses.some(response => !response.text || typeof response.requestNumber !== 'number')) { 84 | console.error('Invalid responses format', { name, messages, responses }); // gpt_pilot_debugging_log 85 | return res.status(400).send('Invalid responses data format.'); 86 | } 87 | 88 | const newConversation = new Conversation({ 89 | name, 90 | messages, 91 | responses 92 | }); 93 | 94 | await newConversation.save(); 95 | console.log('Conversation saved:', newConversation.name); 96 | res.status(201).json(newConversation); 97 | } catch (error) { 98 | console.error('Failed to save conversation:', error.message, error.stack); // gpt_pilot_debugging_log 99 | res.status(500).json({ error: 'Failed to save conversation', errorDetails: error.message, stack: error.stack }); 100 | } 101 | }); 102 | 103 | app.get('/api/load-conversations', async (req, res) => { 104 | try { 105 | console.log('Loading conversation list.'); 106 | const conversations = await Conversation.find({}, 'name _id'); 107 | console.log('Conversations loaded:', conversations); 108 | res.status(200).json(conversations); 109 | } catch (error) { 110 | console.error('Failed to load conversations:', error.message, error.stack); // gpt_pilot_debugging_log 111 | res.status(500).json({ error: 'Failed to load conversations', errorDetails: error.message, stack: error.stack }); 112 | } 113 | }); 114 | 115 | app.get('/api/load-conversations/:id', async (req, res) => { 116 | try { 117 | console.log(`Loading conversation with ID: ${req.params.id}`); 118 | const conversation = await Conversation.findById(req.params.id); 119 | if (!conversation) { 120 | console.log('Conversation not found with ID:', req.params.id); 121 | return res.status(404).send('Conversation not found.'); 122 | } 123 | console.log(`Conversation loaded: ${conversation.name}`); 124 | res.status(200).json(conversation); 125 | } catch (error) { 126 | console.error('Failed to load conversation:', error.message, error.stack); 127 | res.status(500).json({ error: 'Failed to load conversation', errorDetails: error.message, stack: error.stack }); 128 | } 129 | }); 130 | 131 | app.delete('/api/delete-response/:conversationId/:responseId', async (req, res) => { 132 | // Extract parameters and log them 133 | const { conversationId, responseId } = req.params; 134 | console.log(`Processing DELETE request for conversation ID: ${conversationId} and response ID: ${responseId}.`); // gpt_pilot_debugging_log 135 | 136 | if (!mongoose.Types.ObjectId.isValid(conversationId) || !mongoose.Types.ObjectId.isValid(responseId)) { 137 | console.error('Invalid ID format for MongoDB ObjectId', { conversationId, responseId }); // gpt_pilot_debugging_log 138 | return res.status(400).json({ message: 'Invalid ID format for MongoDB ObjectId.' }); 139 | } 140 | 141 | try { 142 | const conversation = await Conversation.findById(conversationId); 143 | if (!conversation) { 144 | console.error(`Conversation not found with ID: ${conversationId}`); // gpt_pilot_debugging_log 145 | return res.status(404).json({ message: 'Conversation not found.' }); 146 | } 147 | 148 | const responseIndex = conversation.responses.findIndex(response => response._id.toString() === responseId); 149 | if (responseIndex === -1) { 150 | console.error(`Response not found with ID: ${responseId} in conversation: ${conversation.name}`); // gpt_pilot_debugging_log 151 | return res.status(404).json({ message: 'Response not found.' }); 152 | } 153 | 154 | conversation.responses.splice(responseIndex, 1); 155 | await conversation.save(); 156 | 157 | console.log(`Response with ID: ${responseId} has been successfully deleted from conversation: ${conversationId}.`); // gpt_pilot_debugging_log 158 | res.status(200).json({ message: 'Response deleted successfully.' }); 159 | } catch (error) { 160 | console.error('Error occurred:', error.message, error.stack); // gpt_pilot_debugging_log 161 | res.status(500).json({ error: 'An error occurred while deleting the response.', details: error.message, stack: error.stack }); 162 | } 163 | }); 164 | 165 | app.delete('/api/delete-conversation/:conversationId', async (req, res) => { 166 | const { conversationId } = req.params; 167 | console.log(`Processing DELETE request for conversation ID: ${conversationId}.`); // gpt_pilot_debugging_log 168 | 169 | if (!conversationId) { 170 | console.log(`Conversation ID not provided for deletion.`); 171 | return res.status(400).json({ message: 'Conversation ID is required.' }); 172 | } 173 | 174 | try { 175 | const deletedConversation = await Conversation.findOneAndDelete({ _id: conversationId }); 176 | if (!deletedConversation) { 177 | console.log(`Failed to find and delete conversation with ID: ${conversationId}`); 178 | return res.status(404).json({ message: 'Conversation not found.' }); 179 | } 180 | 181 | console.log(`Conversation with ID: ${conversationId} has been successfully deleted.`); 182 | res.status(200).json({ message: `Successfully deleted conversation with ID: ${conversationId}.` }); 183 | } catch (error) { 184 | console.error('Error occurred:', error.message, error.stack); // gpt_pilot_debugging_log 185 | res.status(500).json({ error: 'An error occurred while deleting the conversation.', details: error.message, stack: error.stack }); 186 | } 187 | }); 188 | 189 | const PORT = 3000; 190 | 191 | server.listen(PORT, () => { 192 | console.log(`Server running on http://localhost:${PORT} with WebSocket support`); 193 | }); -------------------------------------------------------------------------------- /testOpenAiService.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { callOpenAiApi } = require('./openAiService'); 3 | const { Server } = require('socket.io'); 4 | const http = require('http'); 5 | 6 | // Create a mock server and socket io instance for testing 7 | const server = http.createServer(); 8 | const io = new Server(server); 9 | 10 | // Mock Socket.io emit to log events to the console 11 | io.emit = (eventName, data) => { 12 | console.log(`Event: ${eventName}`, data); 13 | }; 14 | 15 | const mockMessages = [ 16 | { text: 'Hello, who are you?', sender: 'user' }, 17 | { text: 'I am an AI created by OpenAI.', sender: 'assistant' } 18 | ]; 19 | 20 | // Mock request ID 21 | const requestId = 1; 22 | 23 | // Call the function with mock data 24 | callOpenAiApi(mockMessages, io, requestId) 25 | .then(() => console.log('Test completed')) 26 | .catch(error => console.error('Test failed:', error)); -------------------------------------------------------------------------------- /tests/openAiService.test.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const MockAdapter = require('axios-mock-adapter'); 3 | const { callOpenAiApi } = require('../openAiService'); 4 | const { Server } = require('socket.io'); 5 | const io = require('socket.io-client'); 6 | 7 | const mockAxios = new MockAdapter(axios); 8 | 9 | describe('callOpenAiApi', () => { 10 | let server, client; 11 | 12 | beforeAll((done) => { 13 | server = new Server(); 14 | server.listen(3001); // using different port just for test server 15 | client = io.connect(`http://localhost:3001`, { 16 | 'reconnection delay': 0, 17 | 'reopen delay': 0, 18 | 'force new connection': true, 19 | transports: ['websocket'] 20 | }); 21 | client.on('connect', done); 22 | }); 23 | 24 | afterAll(() => { 25 | if (client) client.close(); 26 | if (server) server.close(); 27 | }); 28 | 29 | afterEach(() => { 30 | if (client) client.disconnect(); 31 | if (server) { 32 | server.close(); 33 | server = null; 34 | } 35 | }); 36 | 37 | beforeEach(() => { 38 | jest.clearAllMocks(); 39 | mockAxios.reset(); 40 | }); 41 | 42 | test('should emit a response with the correct data on successful API call', async () => { 43 | expect.assertions(2); // we expect two assertions 44 | 45 | const responseData = { 46 | data: { 47 | id: "response-id", 48 | choices: [{ text: "Response from OpenAI" }] 49 | } 50 | }; 51 | 52 | mockAxios.onPost().replyOnce(200, responseData); 53 | 54 | const responsePromise = new Promise((resolve) => { 55 | client.on('openai_response', (data) => { 56 | resolve(data); 57 | }); 58 | }); 59 | 60 | callOpenAiApi([{ text: 'Hello', sender: 'user' }], server, 1); 61 | 62 | const response = await responsePromise; 63 | expect(response.response).toEqual(responseData.data); 64 | expect(response.requestId).toEqual(1); 65 | }); 66 | 67 | test('should emit an error when the OpenAI API request fails', async () => { 68 | expect.assertions(2); // we expect two assertions 69 | 70 | const errorResponse = { 71 | message: 'Error occurred' 72 | }; 73 | 74 | mockAxios.onPost().networkErrorOnce(); 75 | 76 | const errorPromise = new Promise((resolve) => { 77 | client.on('openai_error', (data) => { 78 | resolve(data); 79 | }); 80 | }); 81 | 82 | callOpenAiApi([{ text: 'Invalid request', sender: 'user' }], server, 1); 83 | 84 | const error = await errorPromise; 85 | expect(error.error.message).toEqual(errorResponse.message); 86 | expect(error.requestId).toEqual(1); 87 | }); 88 | }); -------------------------------------------------------------------------------- /tests/sample.test.js: -------------------------------------------------------------------------------- 1 | test('Sample test', () => expect(true).toBeTruthy()); --------------------------------------------------------------------------------