lokiai / playground.html
ParthSadaria's picture
Update playground.html
a10543b verified
raw
history blame
23.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loki.AI Playground</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;500;600&display=swap"
rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Encode+Sans:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Protest+Revolution&display=swap');
:root {
--bg-dark: #0a0a0f;
--bg-darker: #040409;
--bg-deepest: #020205;
--primary-blue: #4a6cf7;
--secondary-blue: #6678e3;
--accent-color: #7e57c2;
--text-light: #e0e0e8;
--text-muted: #8a8a9b;
--border-color: #1a1a2e;
--hover-color: rgba(78, 108, 247, 0.1);
--delete-red: #ff4d4d;
--header-height: 60px;
--input-height: 80px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
scrollbar-width: thin;
scrollbar-color: var(--primary-blue) transparent;
}
*::-webkit-scrollbar {
width: 8px;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: var(--primary-blue);
border-radius: 20px;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-dark);
color: var(--text-light);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
perspective: 2000px;
}
.chat-wrapper {
position: relative;
width: 100%;
max-width: 800px;
height: 90vh;
background-color: var(--bg-darker);
border-radius: 24px;
overflow: hidden;
box-shadow: 0 20px 50px rgba(5, 5, 10, 0.7);
display: flex;
flex-direction: column;
opacity: 1;
transform: scale(1);
transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
border: 1px solid var(--border-color);
}
.chat-header {
background-color: var(--bg-deepest);
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
height: var(--header-height);
}
.chat-container {
display: none;
flex-direction: column;
height: calc(100% - var(--header-height));
position: relative;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
background-color: var(--bg-dark);
font-family: 'JetBrains Mono', monospace;
height: calc(100% - var(--input-height));
margin-bottom: var(--input-height);
}
.chat-input {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: var(--bg-darker);
border-top: 1px solid var(--border-color);
padding: 16px;
height: var(--input-height);
display: flex;
gap: 12px;
z-index: 10;
align-items: center;
}
.chat-input-container {
position: relative;
flex: 1;
display: flex;
align-items: center;
}
.chat-input input {
width: 100%;
padding: 12px 16px;
padding-right: 50px;
/* Space for send icon */
background-color: rgba(30, 30, 50, 0.8);
border: 1px solid var(--border-color);
border-radius: 12px;
color: var(--text-light);
font-size: 14px;
font-family: 'JetBrains Mono', monospace;
outline: none;
}
.chat-input input:focus {
border-color: var(--primary-blue);
box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.2);
}
.message {
max-width: 80%;
width: fit-content;
padding: 12px 18px;
border-radius: 12px;
font-size: 14px;
line-height: 1.5;
position: relative;
animation: fadeIn 0.4s forwards;
margin-bottom: 8px;
}
.message.user {
align-self: flex-end;
background-color: var(--primary-blue);
color: white;
}
.message.bot {
align-self: flex-start;
background-color: rgba(40, 40, 70, 0.8);
color: var(--text-light);
border: 1px solid var(--border-color);
}
.message::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.1));
opacity: 0;
transition: opacity 0.3s ease;
}
.message:hover::before {
opacity: 1;
}
.initial-input {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 24px;
text-align: center;
background: linear-gradient(145deg, var(--bg-dark), var(--bg-darker));
}
.initial-input h2 {
font-size: 24px;
margin-bottom: 16px;
color: var(--text-light);
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
}
.input-container {
width: 100%;
max-width: 400px;
position: relative;
}
.initial-input input {
width: 100%;
padding: 16px 24px;
background-color: rgba(30, 30, 50, 0.8);
border: 1px solid var(--border-color);
border-radius: 12px;
color: var(--text-light);
font-size: 16px;
font-family: 'JetBrains Mono', monospace;
outline: none;
transition: all 0.3s ease;
}
.send-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background-color: var(--primary-blue);
border-radius: 8px;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
}
.send-icon:hover {
background-color: var(--accent-color);
transform: translateY(-50%) scale(1.05);
border-radius: 15px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Header actions */
.header-actions {
display: flex;
align-items: center;
gap: 10px;
}
.model-select {
background-color: black;
color: var(--text-light);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 10px 14px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.clear-chat {
background-color: var(--delete-red);
color: white;
border: none;
border-radius: 8px;
padding: 10px 14px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 5px;
}
.clear-chat:hover {
background-color: #ff6666;
}
.watermark {
position: absolute;
bottom: 10px;
right: 15px;
color: var(--text-muted);
font-size: 10px;
opacity: 0.5;
transition: opacity 0.3s ease;
z-index: 11;
}
.watermark:hover {
opacity: 0.8;
}
/* Select2 customization */
.select2-container--default .select2-selection--single {
background-color: #333;
color: white;
border: 1px solid #555;
font-size: 14px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
background-color: #333;
color: white;
}
.select2-dropdown {
background-color: #333;
color: white;
max-height: 300px;
overflow-y: auto;
}
.select2-container--default .select2-results__option {
background-color: #333;
color: white;
}
.select2-container--default .select2-results__option--highlighted {
background-color: #555;
color: white;
}
</style>
</head>
<body>
<div class="chat-wrapper">
<div class="chat-header">
<h2 style="font-family: 'JetBrains Mono', monospace;">LOKI.AI Playground</h2>
<div class="header-actions">
<select id="modelSelect" class="model-select">
<option value="gpt-4o-mini">GPT-4o Mini</option>
<option value="gpt-4o">GPT-4o</option>
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
<option value="claude-3-haiku">Claude 3 Haiku</option>
<option value="llama-3.1-8b">Llama 3.1 8B</option>
<option value="llama-3.1-70b">Llama 3.1 70B</option>
<option value="llama-3.1-405b">Llama 3.1 405b</option>
<option value="gemini-1.5-flash">Gemini 1.5 Flash</option>
<option value="gemini-pro">Gemini Pro</option>
<option value="searchgpt">SearchGPT(Realtime Stuff)</option>
<option value="mixtral-8x7b">Mixtral 8x7b</option>
<option value="command-r">Command-R</option>
<option value="command">Command</option>
</select>
<button id="clearChatButton" class="clear-chat">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
Clear
</button>
</div>
</div>
<div class="initial-input">
<h2>Welcome to LOKI.AI</h2>
<div class="input-container">
<input type="text" id="initialChatInput" placeholder="What can I help you with today?">
<div class="send-icon" id="initialSendIcon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line>
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</div>
</div>
</div>
<div class="chat-container" id="chatContainer">
<div class="chat-messages" id="chatMessages"></div>
<div class="chat-input">
<input type="text" id="chatInput" placeholder="Type your message...">
<button id="sendButton"
style="padding: 10px 10px; background-color: black;color: #e0e0e8; font-family: 'JetBrains Mono'; border: solid #1a1a2e 2px; border-radius: 15px;">Send</button>
</div>
</div>
</div>
<div class="watermark">
Made with ❤️ by Parth Sadaria
</div>
<script>
const chatWrapper = document.querySelector('.chat-wrapper');
const initialInput = document.querySelector('.initial-input');
const chatContainer = document.getElementById('chatContainer');
const initialChatInput = document.getElementById('initialChatInput');
const initialSendIcon = document.getElementById('initialSendIcon');
const chatMessages = document.getElementById('chatMessages');
const chatInput = document.getElementById('chatInput');
const sendButton = document.getElementById('sendButton');
const modelSelect = document.getElementById('modelSelect');
const clearChatButton = document.getElementById('clearChatButton');
let currentStreamingMessage = null;
let conversationHistory = [];
function scrollToBottom() {
const chatMessages = document.getElementById('chatMessages');
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function appendMessage(content, type = 'bot', isStreaming = false) {
if (type === 'bot' && isStreaming) {
if (!currentStreamingMessage) {
currentStreamingMessage = document.createElement('div');
currentStreamingMessage.className = `message ${type}`;
chatMessages.appendChild(currentStreamingMessage);
}
// Replace consecutive numbers followed by spaces with numbered list format
// Convert text between ** to bold, handling multiple occurrences
const formattedContent = content
.replace(/(\d+)\.\s*/g, '<br>$1. ') // Ensure proper spacing and line breaks
.replace(/\n/g, '<br>') // Convert remaining newlines
.replace(/\*\*(.*?)\*\*/g, function(match, p1) {
return '<strong>' + p1 + '</strong>';
});
// Safely set innerHTML
currentStreamingMessage.innerHTML += formattedContent;
chatMessages.scrollTop = chatMessages.scrollHeight;
} else {
// Similar logic for non-streaming messages
if (currentStreamingMessage) {
currentStreamingMessage = null;
}
const messageBox = document.createElement('div');
messageBox.className = `message ${type}`;
// Format content similarly
const formattedContent = content
.replace(/(\d+)\.\s*/g, '<br>$1. ')
.replace(/\n/g, '<br>')
.replace(/\*\*(.*?)\*\*/g, function(match, p1) {
return '<strong>' + p1 + '</strong>';
});
messageBox.innerHTML = formattedContent;
chatMessages.appendChild(messageBox);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Update conversation history
conversationHistory.push({
role: type === 'user' ? 'user' : 'assistant',
content: content
});
}
}
$(document).ready(function () {
$('#modelSelect').select2({
placeholder: 'Select a model', // Placeholder text
minimumResultsForSearch: 1 // Show search when there is at least 1 item
});
});
function clearChat() {
chatMessages.innerHTML = '';
conversationHistory = [];
initialInput.style.display = 'flex';
chatContainer.style.display = 'none';
chatWrapper.classList.remove('active');
}
async function sendInitialMessage() {
const userMessage = initialChatInput.value.trim();
const selectedModel = modelSelect.value;
if (!userMessage) return;
initialInput.style.display = 'none';
chatContainer.style.display = 'flex';
chatWrapper.classList.add('active');
appendMessage(userMessage, 'user');
initialChatInput.value = '';
scrollToBottom();
try {
await callApi(userMessage, selectedModel);
} catch (error) {
appendMessage("Oops! Something went wrong.", 'bot');
console.error("API Error:", error);
}
}
async function sendMessage() {
const userMessage = chatInput.value.trim();
const selectedModel = modelSelect.value;
if (!userMessage) return;
appendMessage(userMessage, 'user');
chatInput.value = '';
try {
await callApi(userMessage, selectedModel);
} catch (error) {
appendMessage("Oops! Something went wrong.", 'bot');
console.error("API Error:", error);
}
}
async function callApi(userMessage, model) {
let fullResponse = "";
if (model === "searchgpt") {
const url = `https://parthsadaria-lokiai.hf.space/searchgpt?q=${encodeURIComponent(userMessage)}&stream=true&systemprompt=You are **SearchGPT**, an AI with internet access. Reply directly and accurately to user requests.`;
try {
const response = await fetch(url);
if (response.ok) {
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let done = false;
while (!done) {
const { value, done: streamDone } = await reader.read();
done = streamDone;
if (value) {
const chunk = decoder.decode(value);
const cleanedChunk = chunk.trim().replace(/^data:\s*/, '');
const jsonChunks = cleanedChunk.split("data:").filter(Boolean);
jsonChunks.forEach(jsonString => {
try {
const jsonData = JSON.parse(jsonString);
const content = jsonData.choices?.[0]?.message?.content || "";
if (content) {
// Comprehensive newline conversion
const formattedContent = content
.replace(/\r\n/g, '<br>') // Windows-style newlines
.replace(/\n/g, '<br>') // Unix/Linux-style newlines
.replace(/\r/g, '<br>'); // Old Mac-style newlines
appendMessage(formattedContent, 'bot', true);
}
} catch (err) {
console.warn("Parsing error:", err);
}
});
}
}
} else {
throw new Error(`API responded with status ${response.status}`);
}
} catch (error) {
console.error("API call error:", error);
throw error;
}
} else {
// Similar changes for other models
const url = "https://parthsadaria-lokiai.hf.space/chat/completions";
const payload = {
model: model,
messages: [
...conversationHistory,
{ role: "user", content: userMessage }
],
stream: true
};
const headers = {
"Content-Type": "application/json"
};
try {
const response = await fetch(url, {
method: "POST",
headers: headers,
body: JSON.stringify(payload)
});
if (response.ok) {
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let done = false;
while (!done) {
const { value, done: streamDone } = await reader.read();
done = streamDone;
if (value) {
const chunk = decoder.decode(value);
const cleanedChunk = chunk.trim().replace(/^data:\s*/, '');
const jsonChunks = cleanedChunk.split("data:").filter(Boolean);
jsonChunks.forEach(jsonString => {
try {
const jsonData = JSON.parse(jsonString);
const delta = jsonData.choices?.[0]?.delta || {};
let content = delta.content || "";
// Comprehensive newline conversion
content = content
.replace(/\r\n/g, '<br>') // Windows-style newlines
.replace(/\n/g, '<br>') // Unix/Linux-style newlines
.replace(/\r/g, '<br>'); // Old Mac-style newlines
if (content) {
appendMessage(content, 'bot', true);
}
} catch (err) {
console.warn("Parsing error:", err);
}
});
}
}
} else {
throw new Error(`API responded with status ${response.status}`);
}
} catch (error) {
console.error("API call error:", error);
throw error;
}
}
return fullResponse.trim();
}
// Event Listeners
initialSendIcon.addEventListener('click', sendInitialMessage);
initialChatInput.addEventListener('keypress', (event) => {
if (event.key === 'Enter') sendInitialMessage();
});
sendButton.addEventListener('click', sendMessage);
chatInput.addEventListener('keypress', (event) => {
if (event.key === 'Enter') sendMessage();
});
// Clear Chat Button Event Listener
clearChatButton.addEventListener('click', clearChat);
</script>
</body>
<!-- random
comments
to get
669 lines -->
</html>