diff --git a/public/chat.css b/public/chat.css index a6c45f8..cfe91ab 100644 --- a/public/chat.css +++ b/public/chat.css @@ -431,28 +431,32 @@ body { /* Styles for the hidden sidebar */ #sidebar { display: none; - width: 250px; + width: 300px; position: fixed; top: 0; left: 0; height: 100%; background-color: #2c2c2c; /* Dark mode background */ color: #fff; /* Dark mode text color */ - overflow-x: hidden; - padding-top: 20px; + overflow-x: auto; + overflow-y: auto; /* Enable vertical scroll if content overflows */ + padding-top: 10px; transition: 0.3s; z-index: 1000; /* Ensure sidebar is above other elements */ } + #sidebar a { - padding: 10px 15px; + padding: 5px 10px; text-decoration: none; - font-size: 18px; + font-size: 16px; color: #fff; display: block; } + #sidebar a:hover { background-color: #575757; } + #toggleArrow { position: fixed; top: 50%; @@ -468,6 +472,38 @@ body { transition: 0.3s; z-index: 1001; /* Ensure arrow is above other elements */ } + #toggleArrow:hover { background-color: rgba(0, 0, 0, 0.7); -} \ No newline at end of file +} + +#summariesButton { + position: absolute; + bottom: 20px; + left: 10px; + width: calc(100% - 20px); + background-color: #4CAF50; + color: white; + border: none; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: block; + font-size: 16px; + margin: 4px 2px; + cursor: pointer; + border-radius: 12px; + } + +#summariesButton:hover { + background-color: #45a049; +} + +#chatList { + list-style-type: none; /* Remove bullet points */ + font-size: 9px; +} + +#chatList li { + padding: 5px 10px; /* Add some padding to list items */ +} diff --git a/public/portal.html b/public/portal.html index 7d8ce23..05e5261 100644 --- a/public/portal.html +++ b/public/portal.html @@ -13,7 +13,11 @@
- +
Select a Model
diff --git a/public/script.js b/public/script.js index 6d4769d..df04f9b 100644 --- a/public/script.js +++ b/public/script.js @@ -1277,15 +1277,21 @@ document.getElementById('open-router-model-cohere-command-r-plus').addEventListe const sidebar = document.getElementById('sidebar'); const toggleArrow = document.getElementById('toggleArrow'); + const summariesButton = document.getElementById('summariesButton'); + let summariesOnly = true; // Fetch the list of chats from the backend and display them in the sidebar async function fetchChatList() { try { const response = await fetch('/listChats'); const data = await response.json(); - sidebar.innerHTML = data.files.map(file => { - const fileNameWithoutExt = file.replace('.txt', ''); - return `${fileNameWithoutExt}`; + const chatList = document.getElementById('chatList'); + chatList.innerHTML = data.files.map(file => { + // Remove the .txt extension + let fileNameWithoutExt = file.replace('.txt', ''); + // Replace underscores with spaces + let displayName = fileNameWithoutExt.replace(/_/g, ' '); + return `
  • ${displayName}
  • `; }).join(''); } catch (error) { console.error('Error fetching chat list:', error); @@ -1293,26 +1299,30 @@ document.getElementById('open-router-model-cohere-command-r-plus').addEventListe } // Handle chat selection - sidebar.addEventListener('click', async (event) => { - const chatName = event.target.getAttribute('data-chat'); - if (chatName) { - try { - await fetch('/setChat', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ chosenChat: `${chatName}` }) - }); - - const summaryResponse = await fetch(`/getSummary/${chatName}`); - const summaryData = await summaryResponse.json(); - - if (summaryData.summary) { - displayMessage(summaryData.summary, 'response', false); - } else { - console.error('Summary not found.'); + document.getElementById('chatList').addEventListener('click', async (event) => { + if (event.target.tagName === 'A') { + event.preventDefault(); + const chatName = event.target.getAttribute('data-chat'); + if (chatName) { + try { + console.log("clicked"); + await fetch('/setChat', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ chosenChat: `${chatName}` }) + }); + + const summaryResponse = await fetch(`/getSummary/${chatName}`); + const summaryData = await summaryResponse.json(); + + if (summaryData.summary) { + displayMessage(summaryData.summary, 'response'); + } else { + console.error('Summary not found.'); + } + } catch (error) { + console.error('Error setting chat:', error); } - } catch (error) { - console.error('Error setting chat:', error); } } }); @@ -1328,6 +1338,24 @@ document.getElementById('open-router-model-cohere-command-r-plus').addEventListe } }); + // Handle summariesButton click + summariesButton.addEventListener('click', async () => { + summariesOnly = !summariesOnly; + summariesButton.textContent = summariesOnly ? 'Summaries Only' : 'Whole Conversations'; + + try { + await fetch('/setSummariesOnly', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ summariesOnly }) + }); + } catch (error) { + console.error('Error setting summaries only:', error); + } + }); + + + document.addEventListener('keydown', (event) => { // SHIFT+ESC for focusing the chat input diff --git "a/public/uploads/chats/\"Automated_Daily_Email_Reports\".txt" b/public/uploads/chats/Automated_Daily_Email_Reports.txt similarity index 100% rename from "public/uploads/chats/\"Automated_Daily_Email_Reports\".txt" rename to public/uploads/chats/Automated_Daily_Email_Reports.txt diff --git a/server.js b/server.js index 6d9f5a3..5d48373 100644 --- a/server.js +++ b/server.js @@ -327,6 +327,7 @@ app.post('/generate-image', async (req, res) => { let continueConv = false; let chosenChat = ''; +let summariesOnly = true; // Default to summaries only let conversationHistory = []; @@ -348,10 +349,13 @@ async function initializeConversationHistory() { const fileInstructions = await readInstructionsFile(); systemMessage = `You are a helpful and intelligent AI assistant, knowledgeable about a wide range of topics and highly capable of a great many tasks.\n Specifically:\n ${fileInstructions}`; if (continueConv) { - const contextAndSummary = await continueConversation(chosenChat); - systemMessage += `\n---\n${contextAndSummary}`; - // CHOICE: comment out the above and uncomment the following if you want the whole conversation - // systemMessage = await continueConversation(chosenChat); + if (summariesOnly) { + const contextAndSummary = await continueConversation(chosenChat); + systemMessage += `\n---\n${contextAndSummary}`; + } else { + systemMessage = await continueConversation(chosenChat); + } + } conversationHistory.push({ role: "system", content: systemMessage }); return systemMessage; @@ -385,10 +389,13 @@ async function initializeGeminiConversationHistory() { const geminiMessage = await readGeminiFile(); let systemMessage = 'System Prompt: ' + geminiMessage; if (continueConv) { - const contextAndSummary = await continueConversation(chosenChat); - systemMessage += `\n---\n${contextAndSummary}`; - // CHOICE: comment out the above and uncomment the following if you want the whole conversation - // systemMessage = await continueConversation(chosenChat); + if (summariesOnly) { + const contextAndSummary = await continueConversation(chosenChat); + systemMessage += `\n---\n${contextAndSummary}`; + } else { + systemMessage = await continueConversation(chosenChat); + } + } geminiHistory += systemMessage + '\n'; } catch (error) { @@ -406,18 +413,20 @@ async function continueConversation(chosenChat) { try { // Read the chosen chat file const conversationFile = await fs.promises.readFile(path.join(__dirname, 'public/uploads/chats', `${chosenChat}.txt`), 'utf8'); - // return conversationFile - // CHOICE: - // REMOVE THE above line if you want the AI to have the full context of the conversation. - // Regex to extract everything starting from CONTEXT - const regex = /\n\n-----\n\n(.+)/s; - const match = conversationFile.match(regex); - if (match && match[1]) { - const contextAndSummary = match[1]; - return contextAndSummary; + if (summariesOnly) { + return conversationFile } else { - throw new Error('Context and summary not found in the conversation file.'); + // Regex to extract everything starting from CONTEXT + const regex = /\n\n-----\n\n(.+)/s; + const match = conversationFile.match(regex); + if (match && match[1]) { + const contextAndSummary = match[1]; + return contextAndSummary; + } else { + throw new Error('Context and summary not found in the conversation file.'); + } } + } catch (error) { console.error('Error in continueConversation:', error); throw error; @@ -479,6 +488,17 @@ app.get('/getSummary/:chatName', async (req, res) => { } }); +// Endpoint to set summaries only +app.post('/setSummariesOnly', (req, res) => { + try { + summariesOnly = req.body.summariesOnly; + res.status(200).json({ message: 'Summaries only setting updated successfully', summariesOnly }); + } catch (error) { + console.error('Error in /setSummariesOnly endpoint:', error); + res.status(500).json({ message: 'Failed to update summaries only setting', error: error.message }); + } +}); + // Function to convert conversation history to HTML async function exportChatToHTML() { // Log the current state of both conversation histories before deciding which one to use @@ -585,7 +605,7 @@ async function titleChat(history, tokens, cost) { const completion = await openai.chat.completions.create({ model: 'gpt-4o', messages: [ - { role: 'system', content: 'You will be given the contents of a conversation between a Human and an AI Assistant. Please title this chat by summarizing the topic of the conversation in under 5 plaintext words. Ignore the System Message and focus solely on the User-AI interaction. This will be the name of the file saved via Node, so keep it *extremely* short and concise! Examples: "Friendly AI Assistance", "Install Plex Media Server", "App Layout Feedback", "Calculating Indefinite Integrals", or "Total Cost Calculation", etc. The title should resemble a quick and easy reference point for the User to remember the conversation, and follow smart and short naming conventions.' }, + { role: 'system', content: 'You will be given the contents of a conversation between a Human and an AI Assistant. Please title this chat by summarizing the topic of the conversation in under 5 plaintext words. Ignore the System Message and focus solely on the User-AI interaction. This will be the name of the file saved via Node, so keep it *extremely* short and concise! Examples: "Friendly AI Assistance", "Install Plex Media Server", "App Layout Feedback", "Calculating Indefinite Integrals", or "Total Cost Calculation", etc. The title should resemble a quick and easy reference point for the User to remember the conversation, and follow smart and short naming conventions. Do NOT use any special symbols; simply return the words in plaintext without any formatting, markdown, quotes, etc. The title needs to be compatible with a Node.js filename.' }, { role: 'user', content: history } ] }); @@ -608,12 +628,15 @@ async function titleChat(history, tokens, cost) { // Define the full file path const filePath = path.join(folderPath, `${title}.txt`); - const chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; - // const chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Above is the conversation between the User -- a Human -- and an AI Assistant (yourself). A summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + let chatText; + if (summariesOnly) { + chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + } else { + chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Above is the conversation between the User -- a Human -- and an AI Assistant (yourself). A summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + } fs.writeFileSync(filePath, chatText); -// CHOICE: uncomment the second chatText if you want the whole conversation, comment out the first to undo summary-only mode -// theory... +// test... // const chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Above may be the conversation between the User -- a Human -- and an AI Assistant (yourself); if you do not see it, the User has decided to display only a summary. The summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; @@ -931,10 +954,12 @@ async function initializeClaudeInstructions() { let instructions = await readClaudeFile(); claudeInstructions = `${instructions}`; if (continueConv) { - const contextAndSummary = await continueConversation(chosenChat); - systemMessage += `\n---\n${contextAndSummary}`; - // CHOICE: comment out the above and uncomment the following if you want the whole conversation - // systemMessage = await continueConversation(chosenChat); + if (summariesOnly) { + const contextAndSummary = await continueConversation(chosenChat); + systemMessage += `\n---\n${contextAndSummary}`; + } else { + systemMessage = await continueConversation(chosenChat); + } } } @@ -1273,7 +1298,7 @@ async function nameChat(chatHistory, tokens) { const googleModel = genAI.getGenerativeModel({ model: 'gemini-1.5-flash', generationConfig: defaultConfig, safetySettings }); // Generate content based on the geminiHistory - const answer = await googleModel.generateContent(`You will be given the contents of a conversation between a Human and an AI Assistant. Please title this chat by summarizing the topic of the conversation in under 5 plaintext words. Ignore the System Message and focus on the User-AI interaction. This will be the name of the file saved via Node, so keep it *extremely* short and concise! Examples: "Friendly AI Assistance", "Install Plex Media Server", "App Layout Feedback", "Calculating Indefinite Integrals", or "Total Cost Calculation", etc. The title should resemble a quick and easy reference point for the User to remember the conversation, and follow smart and short naming conventions.\n\n${chatHistory}`); + const answer = await googleModel.generateContent(`You will be given the contents of a conversation between a Human and an AI Assistant. Please title this chat by summarizing the topic of the conversation in under 5 plaintext words. Ignore the System Message and focus solely on the User-AI interaction. This will be the name of the file saved via Node, so keep it *extremely* short and concise! Examples: "Friendly AI Assistance", "Install Plex Media Server", "App Layout Feedback", "Calculating Indefinite Integrals", or "Total Cost Calculation", etc. The title should resemble a quick and easy reference point for the User to remember the conversation, and follow smart and short naming conventions. Do NOT use any special symbols; simply return the words in plaintext without any formatting, markdown, quotes, etc. The title needs to be compatible with a Node.js filename.\n\n${chatHistory}`); title = answer.response.text().trim().replace(/ /g, '_'); @@ -1291,12 +1316,15 @@ async function nameChat(chatHistory, tokens) { // Define the full file path const filePath = path.join(folderPath, `${title}.txt`); - const chatText = `${chatHistory}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $0.00!\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; - // const chatText = `${chatHistory}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $0.00!\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). A summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + let chatText; + if (summariesOnly) { + chatText = `${chatHistory}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $0.00!\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + } else { + chatText = `${chatHistory}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $0.00!\n\n-----\n\nCONTEXT: Below is a summary of the conversation between the User -- a Human -- and an AI Assistant (yourself). A summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`; + } fs.writeFileSync(filePath, chatText); -// CHOICE: uncomment the second chatText if you want the whole conversation, comment out the first to undo summary-only mode -// theory... +// test... // const chatText = `${history}\n\nTotal Tokens: ${tokens.totalTokens}\nTotal Cost: $${cost.toFixed(6)}\n\n-----\n\nCONTEXT: Above may be the conversation between the User -- a Human -- and an AI Assistant (yourself); if you do not see it, the User has decided to display only a summary. The summary of said conversation is below for you to reference. INSTRUCTION: The User will send a message/prompt with the expectation that you will pick up where you left off and seamlessly continue the conversation. Do not give any indication that the conversation had paused or resumed; simply answer the User's next query in the context of the above Chat, inferring the Context and asking for additional information if necessary.\n---\nConversation Summary: ${summary}`;