lokiai / playground.html
ParthSadaria's picture
new model sonnet🔥🔥🔥🔥
0b458bb verified
raw
history blame
29.5 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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.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: 1000px;
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;
justify-content: center;
}
.model-select {
background-color: black;
color: var(--text-light);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 8px 12px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
max-width: 150px;
overflow-y: auto;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
padding-right: 28px;
}
.model-select option {
background-color: var(--bg-dark);
color: var(--text-light);
padding: 8px;
}
.clear-chat {
background-color: var(--delete-red);
color: white;
border: none;
border-radius: 8px;
width: 36px;
height: 36px;
padding: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.clear-chat:hover {
background-color: #ff6666;
}
.clear-chat svg {
width: 18px;
height: 18px;
}
.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;
}
@media screen and (max-width: 768px) {
.chat-header {
padding: 8px 12px;
height: auto;
min-height: 50px;
}
.header-actions {
gap: 8px;
}
.model-select {
font-size: 12px;
padding: 6px 24px 6px 8px;
max-width: 120px;
}
.clear-chat {
width: 32px;
height: 32px;
padding: 6px;
}
.clear-chat svg {
width: 16px;
height: 16px;
}
.chat-input {
padding: 8px;
height: auto;
min-height: 60px;
}
.chat-input input {
padding: 10px 12px;
font-size: 14px;
height: 40px;
}
}
@media screen and (max-width: 480px) {
.chat-header h2 {
font-size: 14px;
}
.message {
font-size: 12px;
}
.header-actions {
flex-direction: row;
gap: 6px;
}
.model-select {
max-width: 100px;
font-size: 11px;
}
.clear-chat {
width: 28px;
height: 28px;
padding: 5px;
}
.clear-chat svg {
width: 14px;
height: 14px;
}
.chat-input input {
font-size: 13px;
height: 36px;
}
.initial-input input {
font-size: 15px;
}
.initial-input h2 {
font-size: 20px;
}
}
/* Additional responsive adjustments to existing CSS */
* {
-webkit-tap-highlight-color: transparent;
}
body {
touch-action: manipulation;
}
input,
button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
@media (hover: hover) {
.send-icon:hover {
background-color: var(--accent-color);
transform: translateY(-50%) scale(1.05);
border-radius: 15px;
}
}
@media (pointer: coarse) {
.send-icon {
width: 45px;
height: 45px;
}
.chat-input input {
font-size: 16px;
/* Larger font for touch devices */
}
}
.model-select::-webkit-scrollbar {
width: 4px;
}
.model-select::-webkit-scrollbar-track {
background: var(--bg-dark);
}
.model-select::-webkit-scrollbar-thumb {
background-color: var(--primary-blue);
border-radius: 4px;
}
.chat-input input:focus,
.initial-input input:focus,
.model-select:focus,
.clear-chat:focus,
.send-icon:focus {
outline: 2px solid var(--primary-blue);
outline-offset: 2px;
}
.github-link {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
z-index: 999;
border-radius: 8px;
background-color: transparent;
border: 1px solid var(--border-color);
transition: all 0.3s ease;
color: var(--text-light);
position: absolute; /* Allows placement in the corner */
bottom: 0; /* Aligns to the top */
left: 0; /* Aligns to the left */
margin: 10px; /* Adds spacing from the edges */
}
.github-link:hover {
background-color: var(--hover-color);
transform: translateY(-2px);
}
.github-link svg {
width: 22px;
height: 22px;
}
</style>
</head>
<body>
<a href="https://github.com/ParthSadaria" target="_blank" rel="noopener noreferrer" class="github-link" aria-label="Visit Parth Sadaria's GitHub profile">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
</a>
<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="searchgpt">SearchGPT(Web-access)</option>
<option value="claude-sonnet-3.5">Claude 3.5 Sonnet</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="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" 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>
</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 isStreamingInProgress = false;
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);
// Add to conversation history only at the start of streaming
if (!isStreamingInProgress) {
conversationHistory.push({
role: 'assistant',
content: ''
});
isStreamingInProgress = true;
}
}
const formattedContent = content
.replace(/(\d+)\.\s*/g, '<br>$1. ')
.replace(/\n/g, '<br>')
.replace(/\*\*(.*?)\*\*/g, (match, p1) => '<strong>' + p1 + '</strong>');
currentStreamingMessage.innerHTML += formattedContent;
// Update the last message in conversation history
if (conversationHistory.length > 0) {
conversationHistory[conversationHistory.length - 1].content += content;
}
chatMessages.scrollTop = chatMessages.scrollHeight;
} else {
if (currentStreamingMessage) {
isStreamingInProgress = false;
currentStreamingMessage = null;
}
const messageBox = document.createElement('div');
messageBox.className = `message ${type}`;
const formattedContent = content
.replace(/(\d+)\.\s*/g, '<br>$1. ')
.replace(/\n/g, '<br>')
.replace(/\*\*(.*?)\*\*/g, (match, p1) => '<strong>' + p1 + '</strong>');
messageBox.innerHTML = formattedContent;
chatMessages.appendChild(messageBox);
// Add to conversation history only for new messages
if (type === 'user') {
conversationHistory.push({
role: 'user',
content: content
});
}
chatMessages.scrollTop = chatMessages.scrollHeight;
}
}
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 {
const url = "https://parthsadaria-lokiai.hf.space/chat/completions";
const payload = {
model: model,
messages: [
...conversationHistory
],
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 800 lines -->
</html>