Skip to content

Commit

Permalink
Create lollms.html
Browse files Browse the repository at this point in the history
  • Loading branch information
ParisNeo authored Jul 24, 2024
1 parent 7fe93e7 commit 410d6a1
Showing 1 changed file with 369 additions and 0 deletions.
369 changes: 369 additions & 0 deletions lollms.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat with LoLLMS</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css" />
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
<style>
#chat {
max-height: 400px;
overflow-y: auto;
border: 1px solid #e2e8f0; /* Light gray border */
border-radius: 0.5rem; /* Rounded corners */
background-color: #f7fafc; /* Light gray background */
padding: 1rem; /* Padding inside the chat area */
}
.message {
padding: 0.5rem;
border-radius: 0.375rem; /* Rounded corners for messages */
margin-bottom: 0.5rem; /* Space between messages */
display: flex; /* Flexbox for icon and message */
align-items: center; /* Center items vertically */
}
.user {
background-color: #cbd5e0; /* Light blue for user messages */
text-align: right; /* Align user messages to the right */
}
.assistant {
background-color: #e6fffa; /* Light green for assistant messages */
text-align: left; /* Align assistant messages to the left */
}
.icon {
width: 30px; /* Icon size */
height: 30px; /* Icon size */
border-radius: 50%; /* Circular icon */
margin-right: 0.5rem; /* Space between icon and message */
}
.settings-modal {
display: none; /* Hidden by default */
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
justify-content: center;
align-items: center;
}
.settings-content {
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.discussion-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #e2e8f0; /* Light gray border */
border-radius: 0.5rem; /* Rounded corners */
background-color: #f7fafc; /* Light gray background */
padding: 1rem; /* Padding inside the discussion list */
margin-bottom: 1rem; /* Space below the discussion list */
}
.discussion-item {
padding: 0.5rem;
border-radius: 0.375rem; /* Rounded corners for discussion items */
margin-bottom: 0.5rem; /* Space between discussion items */
display: flex; /* Flexbox for discussion item */
justify-content: space-between; /* Space between text and button */
align-items: center; /* Center items vertically */
background-color: #e2e8f0; /* Light gray background for items */
}
</style>
</head>
<body class="bg-gray-100 flex items-center justify-center h-screen">
<div class="bg-white rounded-lg shadow-lg p-6 w-full max-w-md">
<h1 class="text-xl font-bold mb-4">Chat with LoLLMS</h1>
<div class="discussion-list" id="discussionList">
<!-- Old discussions will be listed here -->
</div>
<button id="newDiscussionButton" class="mt-2 w-full bg-green-500 text-white p-2 rounded-lg">New Discussion</button>
<div id="chat" class="mb-4 p-4">
<!-- Chat messages will be appended here -->
</div>
<textarea id="userInput" class="w-full p-2 border border-gray-300 rounded-lg" rows="3" placeholder="Type your message..."></textarea>
<button id="sendButton" class="mt-2 w-full bg-blue-500 text-white p-2 rounded-lg">Send</button>
<button id="settingsButton" class="mt-2 w-full bg-gray-500 text-white p-2 rounded-lg">Settings</button>
<div class="mt-4">
<label class="block mb-2">Select Mode:</label>
<select id="modeSelect" class="w-full p-2 border border-gray-300 rounded-lg">
<option value="instruct">Instruct Mode</option>
<option value="chat">Chat Mode</option>
</select>
</div>
</div>

<div id="settingsModal" class="settings-modal flex">
<div class="settings-content">
<h2 class="text-lg font-bold mb-4">Settings</h2>
<label for="apiKey" class="block mb-2">API Key:</label>
<input type="text" id="apiKey" class="w-full p-2 border border-gray-300 rounded-lg mb-4" placeholder="Enter API Key">
<label for="serverAddress" class="block mb-2">Select Server Address:</label>
<select id="serverSelect" class="w-full p-2 border border-gray-300 rounded-lg mb-4"></select>
<input type="text" id="newServerInput" class="w-full p-2 border border-gray-300 rounded-lg mb-4" placeholder="Enter new server address">
<button id="addServerButton" class="mt-2 w-full bg-yellow-500 text-white p-2 rounded-lg">Add Server</button>
<label for="modelSelect" class="block mb-2">Select Model:</label>
<select id="modelSelect" class="w-full p-2 border border-gray-300 rounded-lg mb-4"></select>
<button id="addModelButton" class="mt-2 w-full bg-yellow-500 text-white p-2 rounded-lg">Add Model</button>
<input type="text" id="newModelInput" class="w-full p-2 border border-gray-300 rounded-lg mb-4" placeholder="Enter new model name">
<label for="userIcon" class="block mb-2">Your Icon:</label>
<input type="file" id="userIcon" class="w-full mb-4" accept="image/*">
<label for="assistantIcon" class="block mb-2">Assistant Icon:</label>
<input type="file" id="assistantIcon" class="w-full mb-4" accept="image/*">
<button id="saveSettings" class="w-full bg-blue-500 text-white p-2 rounded-lg">Save</button>
<button id="closeSettings" class="w-full bg-red-500 text-white p-2 rounded-lg mt-2">Close</button>
</div>
</div>

<script>
const defaultApiKey = localStorage.getItem('apiKey') || 'your_api_key_here'; // Default API key
const defaultApiUrl = localStorage.getItem('apiUrl') || 'http://localhost:9600/v1/completions'; // Default server address
const defaultUserIcon = localStorage.getItem('userIcon') || 'https://via.placeholder.com/30'; // Default user icon
const defaultAssistantIcon = localStorage.getItem('assistantIcon') || 'https://via.placeholder.com/30'; // Default assistant icon
const defaultModel = localStorage.getItem('model') || 'gpt-4o-mini'; // Default model
let apiKey = defaultApiKey;
let apiUrl = defaultApiUrl;
let userIcon = defaultUserIcon;
let assistantIcon = defaultAssistantIcon;
let conversationHistory = ""; // To store the conversation history
let discussions = JSON.parse(localStorage.getItem('discussions')) || []; // Load discussions from localStorage
let models = JSON.parse(localStorage.getItem('models')) || ['gpt-4o-mini', 'lollms']; // Load models from localStorage
let servers = JSON.parse(localStorage.getItem('servers')) || ['http://localhost:9600/v1/completions']; // Load servers from localStorage

function renderDiscussionList() {
const discussionList = document.getElementById('discussionList');
discussionList.innerHTML = ''; // Clear the list
discussions.forEach((discussion, index) => {
const discussionItem = document.createElement('div');
discussionItem.className = 'discussion-item';
discussionItem.innerHTML = `
<span>Discussion ${index + 1}</span>
<div>
<button class="view-button bg-blue-500 text-white p-1 rounded-lg" data-index="${index}">View</button>
<button class="remove-button bg-red-500 text-white p-1 rounded-lg ml-2" data-index="${index}">Remove</button>
</div>
`;
discussionList.appendChild(discussionItem);
});
}

function renderModelSelect() {
const modelSelect = document.getElementById('modelSelect');
modelSelect.innerHTML = ''; // Clear existing options
models.forEach(model => {
const option = document.createElement('option');
option.value = model;
option.textContent = model.charAt(0).toUpperCase() + model.slice(1); // Capitalize first letter
modelSelect.appendChild(option);
});
modelSelect.value = defaultModel; // Set the default model
}

function renderServerSelect() {
const serverSelect = document.getElementById('serverSelect');
serverSelect.innerHTML = ''; // Clear existing options
servers.forEach(server => {
const option = document.createElement('option');
option.value = server;
option.textContent = server; // Display server address
serverSelect.appendChild(option);
});
serverSelect.value = apiUrl; // Set the default server
}

document.getElementById('addServerButton').addEventListener('click', () => {
const newServerInput = document.getElementById('newServerInput');
const newServer = newServerInput.value.trim();
if (newServer && !servers.includes(newServer)) {
servers.push(newServer);
localStorage.setItem('servers', JSON.stringify(servers)); // Save servers to localStorage
renderServerSelect(); // Refresh server select options
newServerInput.value = ''; // Clear input
} else {
alert('Server address is either empty or already exists.');
}
});

document.getElementById('addModelButton').addEventListener('click', () => {
const newModelInput = document.getElementById('newModelInput');
const newModel = newModelInput.value.trim();
if (newModel && !models.includes(newModel)) {
models.push(newModel);
localStorage.setItem('models', JSON.stringify(models)); // Save models to localStorage
renderModelSelect(); // Refresh model select options
newModelInput.value = ''; // Clear input
} else {
alert('Model name is either empty or already exists.');
}
});

document.getElementById('newDiscussionButton').addEventListener('click', () => {
conversationHistory = ""; // Reset conversation history for new discussion
renderDiscussionList(); // Refresh the discussion list
document.getElementById('chat').innerHTML = ''; // Clear chat messages
});

document.getElementById('sendButton').addEventListener('click', async () => {
const userInput = document.getElementById('userInput');
const prompt = userInput.value;
const selectedModel = document.getElementById('modelSelect').value;
const selectedMode = document.getElementById('modeSelect').value;
if (!prompt) return;

appendMessage('You: ' + prompt, 'user');
userInput.value = '';

const responseText = await generateText(prompt, selectedModel, selectedMode);
appendMessage('LoLLMS: ' + responseText, 'assistant');
});

document.getElementById('settingsButton').addEventListener('click', () => {
document.getElementById('settingsModal').style.display = 'flex';
document.getElementById('apiKey').value = apiKey;
renderServerSelect(); // Populate server select options
renderModelSelect(); // Populate model select options
});

document.getElementById('saveSettings').addEventListener('click', () => {
apiKey = document.getElementById('apiKey').value;
apiUrl = document.getElementById('serverSelect').value; // Get selected server address
const selectedModel = document.getElementById('modelSelect').value;
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('apiUrl', apiUrl);
localStorage.setItem('model', selectedModel); // Save selected model to local storage
alert('Settings saved!');
document.getElementById('settingsModal').style.display = 'none';
});

document.getElementById('closeSettings').addEventListener('click', () => {
document.getElementById('settingsModal').style.display = 'none';
});

async function generateText(userPrompt, model, mode) {
let data;
if (mode === "chat") {
data = {
model: model,
messages: [{ role: "user", content: userPrompt }],
temperature: 0.7
};
} else {
// Instruct mode
const systemPrompt = "You are a helpful assistant.";
const fullPrompt = `${systemPrompt}\n${conversationHistory}<@>user: ${userPrompt}<@>assistant:`;
data = {
model: model,
prompt: fullPrompt,
stream: true,
temperature: 0.7,
max_tokens: 100
};
}

try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify(data)
});

const responseData = await response.json();

if (mode === "chat") {
return responseData.choices[0].message.content; // Return content from chat mode
} else {
// Handle instruct mode response
let text = '';
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');

while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n').filter(line => line.trim() !== '');

for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonData = line.substring(6).trim(); // Remove 'data: ' prefix
if (jsonData === '[DONE]') {
break; // Stop processing on '[DONE]'
}
try {
const parsedData = JSON.parse(jsonData);
text += parsedData.choices[0].text;
} catch (e) {
console.error('Error parsing JSON:', e);
}
}
}
}

// Update conversation history
conversationHistory += `<@>user: ${userPrompt}<@>assistant: ${text}\n`;
return text;
}
} catch (error) {
console.error('Error:', error);
return 'Error occurred while generating text.';
}
}

function appendMessage(message, sender) {
const chat = document.getElementById('chat');
const messageElement = document.createElement('div');
messageElement.className = `message ${sender}`; // Add class based on sender

const iconUrl = sender === 'user' ? userIcon : assistantIcon;
const name = sender === 'user' ? 'You' : 'LoLLMS';

// Render Markdown and highlight code blocks
const renderedMessage = marked.parse(message);
messageElement.innerHTML = `
<img src="${iconUrl}" alt="${name}'s icon" class="icon">
<div>
<strong>${name}:</strong>
<div>${renderedMessage}</div>
</div>
`; // Render Markdown
chat.appendChild(messageElement);
chat.scrollTop = chat.scrollHeight; // Scroll to the bottom

// Highlight code blocks
Prism.highlightAll();
}

document.getElementById('discussionList').addEventListener('click', (event) => {
if (event.target.classList.contains('view-button')) {
const index = event.target.getAttribute('data-index');
conversationHistory = discussions[index]; // Load the selected discussion
document.getElementById('chat').innerHTML = ''; // Clear chat messages
const messages = conversationHistory.split('\n');
messages.forEach(msg => {
if (msg) {
const [role, content] = msg.split(': ');
appendMessage(`${role}: ${content}`, role.trim());
}
});
} else if (event.target.classList.contains('remove-button')) {
const index = event.target.getAttribute('data-index');
discussions.splice(index, 1); // Remove the selected discussion
localStorage.setItem('discussions', JSON.stringify(discussions)); // Update localStorage
renderDiscussionList(); // Refresh the discussion list
}
});

// Initial render of discussions, models, and servers
renderDiscussionList();
renderModelSelect();
renderServerSelect();
</script>
</body>
</html>

0 comments on commit 410d6a1

Please sign in to comment.