adds a simple WebUI for the evaluation of models

This commit is contained in:
julian 2025-01-29 00:50:22 -05:00
parent bfa6c0e1c1
commit 1f83d279a1
8 changed files with 509 additions and 0 deletions

View File

@ -1,5 +1,6 @@
from fastapi import FastAPI, File, Form, UploadFile, HTTPException from fastapi import FastAPI, File, Form, UploadFile, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse from fastapi.responses import JSONResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
import torch import torch
from transformers import AutoConfig, AutoModelForCausalLM from transformers import AutoConfig, AutoModelForCausalLM
from janus.models import MultiModalityCausalLM, VLChatProcessor from janus.models import MultiModalityCausalLM, VLChatProcessor
@ -8,7 +9,12 @@ import numpy as np
import io import io
import os import os
# Resolve absolute path based on the script's location
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # This gets "demo/"
WEBUI_DIR = os.path.join(BASE_DIR, "webui") # Moves up to the project root
app = FastAPI() app = FastAPI()
app.mount("/webui", StaticFiles(directory=WEBUI_DIR, html=True), name="webui")
# Load model and processor # Load model and processor
model_path = os.getenv("MODEL_NAME", "deepseek-ai/Janus-1.3B") model_path = os.getenv("MODEL_NAME", "deepseek-ai/Janus-1.3B")

164
demo/webui/APIChat.js Normal file
View File

@ -0,0 +1,164 @@
document.addEventListener('DOMContentLoaded', function () {
const apiUrlInput = document.getElementById('apiUrlInput');
const dropdownButton = document.getElementById('dropdownButton');
const dropdownMenu = document.getElementById('dropdownMenu');
const responsesContainer = document.getElementById('responsesContainer');
const promptInput = document.getElementById('promptInput');
const sendButton = document.getElementById('sendButton');
const errorMessage = document.getElementById('errorMessage');
const defaultApiUrl = window.location.origin + '/generate_images';
let apiUrl = localStorage.getItem('lastUsedUrl') || defaultApiUrl;
let urlHistory = JSON.parse(localStorage.getItem('urlHistory')) || [];
// If urlHistory is empty, add the default localhost URL
if (urlHistory.length === 0) {
saveUrl(defaultApiUrl)
}
apiUrlInput.value = apiUrl;
updateDropdown();
function updateDropdown() {
dropdownMenu.innerHTML = urlHistory.length
? urlHistory.map(url => `<div class="dropdown-item">
<span class="select-url">${url}</span>
<button class="delete-button">🗑</button>
</div>`).join('')
: '<div class="dropdown-item">No history</div>';
}
function addMessage(sender, body, avatarUrl) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('chat-message');
const avatarDiv = document.createElement('div');
avatarDiv.classList.add('message-avatar');
avatarDiv.innerHTML = avatarUrl ? `<img src="${avatarUrl}" alt="Avatar">` : `<div class="default-avatar">${sender[0]}</div>`;
const messageContent = document.createElement('div');
messageContent.classList.add('message-content');
// Generate timestamp
const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
messageContent.innerHTML = `
<div class="message-header">
<span class="message-sender">${sender}</span>
<span class="message-timestamp">${timestamp}</span>
</div>
<div class="message-body"></div>
`;
const messageBody = messageContent.querySelector('.message-body');
if (typeof body === 'string') {
messageBody.textContent = body;
} else {
messageBody.appendChild(body);
}
messageDiv.appendChild(avatarDiv);
messageDiv.appendChild(messageContent);
responsesContainer.appendChild(messageDiv);
responsesContainer.scrollTop = responsesContainer.scrollHeight;
}
dropdownButton.addEventListener('click', () => {
dropdownMenu.style.display = dropdownMenu.style.display === 'none' ? 'block' : 'none';
});
dropdownMenu.addEventListener('click', (e) => {
if (e.target.classList.contains('select-url')) {
apiUrl = e.target.textContent;
apiUrlInput.value = apiUrl;
saveUrl(apiUrl);
dropdownMenu.style.display = 'none';
} else if (e.target.classList.contains('delete-button')) {
const parent = e.target.closest('.dropdown-item');
const urlToDelete = parent.querySelector('.select-url').textContent;
urlHistory = urlHistory.filter(url => url !== urlToDelete);
localStorage.setItem('urlHistory', JSON.stringify(urlHistory));
updateDropdown();
}
});
apiUrlInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
apiUrl = apiUrlInput.value.trim(); // Ensure the latest value is saved
saveUrl(apiUrl);
}
});
function saveUrl(url) {
if (!url.trim()) return;
// Set the last used URL
localStorage.setItem('lastUsedUrl', url);
// Add to history if it doesn't exist
if (!urlHistory.includes(url)) {
urlHistory.push(url);
localStorage.setItem('urlHistory', JSON.stringify(urlHistory));
updateDropdown();
}
addMessage('System', `API URL set to: ${url}`, './system.png');
}
async function generateImage() {
const prompt = promptInput.value.trim();
if (!prompt) return;
addMessage('You', prompt, './user.png');
promptInput.value = '';
sendButton.disabled = true;
sendButton.textContent = 'Generating...';
errorMessage.style.display = 'none';
try {
const params = new URLSearchParams();
params.set('prompt', prompt);
params.set('seed', Math.floor(Math.random() * 1000000).toString());
params.set('guidance', '5');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params.toString(),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to generate image. Status: ${response.status}, Response: ${errorText}`);
}
const blob = await response.blob();
const img = document.createElement('img');
img.src = URL.createObjectURL(blob);
img.style.maxWidth = '100%';
img.style.borderRadius = '8px';
addMessage('AI', img, './child.jpg');
} catch (err) {
addMessage('System', `Error generating image: ${err.message}`, './system.png');
errorMessage.textContent = `Error generating image: ${err.message}`;
errorMessage.style.display = 'block';
} finally {
sendButton.disabled = false;
sendButton.textContent = 'Send';
}
}
sendButton.addEventListener('click', generateImage);
promptInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
generateImage();
}
});
});

BIN
demo/webui/child.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
demo/webui/favicon.ico Normal file

Binary file not shown.

304
demo/webui/index.css Normal file
View File

@ -0,0 +1,304 @@
/* Ensure full-screen, no scrolling */
html, body {
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrolling */
background-color: black; /* Match chat background */
font-family: Arial, sans-serif; /* Improve readability */
}
/* Chat container setup */
.chat-container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
box-sizing: border-box;
}
/* Center chat box */
.chat-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #000;
overflow: hidden;
padding: 20px;
}
/* Response messages container */
.responses-container {
overflow-y: auto;
align-items: center;
width: 40%;
height: 100%;
border-radius: 1rem;
border: 1px solid #ccc;
background-color: #1e1e1e;
margin: 0 auto; /* Center horizontally */
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.chat-box {
padding: 10px;
}
.responses-container {
width: 100vw; /* Full width */
max-width: none;
border-radius: 0;
border: none;
height: calc(100vh - 120px); /* Adjust height for input and header */
}
}
/* Input container */
.input-container {
display: flex;
padding: 10px;
width: 100%;
background-color: #242424;
flex-shrink: 0; /* Prevent shrinking */
}
/* Individual chat message */
.chat-message {
display: flex;
align-items: top;
margin-bottom: 5px;
padding: 10px;
word-wrap: break-word;
position: relative; /* For positioning options popup */
}
/* Show message options on hover */
.chat-message:hover .message-options {
display: flex;
}
/* Avatar styling */
.message-avatar img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.message-avatar .default-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #ccc;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
margin-right: 10px;
}
/* Message content */
.message-content {
padding: 0;
width: auto;
max-width: 80%;
margin: 0;
}
/* Sender name */
.message-sender {
font-weight: bold;
margin-bottom: 2px;
color: var(--arkavo-orange, red);
}
/* Timestamp */
.message-timestamp {
font-size: 0.8em;
color: #888;
margin-left: 8px;
}
/* Message body */
.message-body {
background-color: #333;
color:#eee;
padding: 8px 12px;
border-radius: 8px;
word-wrap: break-word;
}
/* Message options */
.message-options {
display: none;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background-color: #444;
border-radius: 4px;
padding: 4px;
gap: 4px;
}
/* Option button */
.option-button {
background-color: #555;
color: #fff;
border: none;
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
}
.option-button:hover {
background-color: #666;
}
/* Chat input field */
.chat-input {
flex: 1;
padding: 10px;
border: 1px solid #444;
border-radius: 8px;
resize: none;
font-size: 1.5rem;
margin-right: 10px;
background-color: #333;
color: #fff;
}
/* Send button */
.send-button {
padding: 10px 15px;
background-color: var(--arkavo-dark-orange, red);
color: #fff;
border: none;
font-size: 1.5rem;
border-radius: 8px;
cursor: pointer;
}
.send-button:hover {
background-color: darkred;
}
/* Combined input field */
.combined-input {
position: relative;
align-items: center;
font-size: 1.5rem;
width: 100%;
}
.combined-input .chat-input {
flex: 1;
padding-right: 40px;
border: 1px solid #ccc;
font-size: 1.5rem;
border-radius: 4px;
padding: 8px;
}
/* Dropdown button */
.dropdown-button {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 40px;
color: #333;
font-size: 1.5rem;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-left: none;
border-radius: 0 4px 4px 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.dropdown-button:hover {
background-color: #e0e0e0;
}
/* Dropdown menu */
.dropdown-menu {
position: absolute;
background: black;
border: 1px solid #ccc;
border-radius: 4px;
max-height: 200px;
overflow-y: auto;
font-size: 1.5rem;
width: 100%;
z-index: 2000;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2);
}
/* Dropdown item */
.dropdown-item {
display: flex;
font-size: 1.5rem;
justify-content: space-between;
align-items: center;
padding: 8px;
background: #333;
color: #f0f0f0;
cursor: pointer;
border-bottom: 1px solid #eee;
}
.dropdown-item:hover {
background: #f0f0f0;
color: #333;
}
/* Delete button */
.delete-button {
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
color: red;
}
.delete-button:hover {
color: darkred;
}
/* Scrollbar for Webkit browsers (Chrome, Edge, Safari) */
::-webkit-scrollbar {
width: 8px; /* Adjust width */
height: 8px; /* Adjust height for horizontal scrollbars */
}
/* Scrollbar track (background) */
::-webkit-scrollbar-track {
background: #1a1a1a; /* Dark background */
border-radius: 4px; /* Rounded corners */
}
/* Scrollbar thumb (draggable handle) */
::-webkit-scrollbar-thumb {
background: #666; /* Greyish thumb */
border-radius: 4px; /* Rounded corners */
transition: background 0.3s ease-in-out;
}
/* Hover effect */
::-webkit-scrollbar-thumb:hover {
background: #888; /* Lighter grey on hover */
}
/* Firefox Scrollbar */
* {
scrollbar-width: thin; /* Thinner scrollbar */
scrollbar-color: #666 #1a1a1a; /* Thumb color, Track color */
}

35
demo/webui/index.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Interface</title>
<link rel="stylesheet" href="./index.css">
<link rel="icon" type="image/png" href="/webui/favicon.ico">
</head>
<body>
<main class="chat-container">
<div class="chat-box">
<div class="input-container">
<div class="combined-input">
<input id="apiUrlInput" class="chat-input" type="text" placeholder="Enter API URL" style="width: 100%;">
<button id="dropdownButton" class="dropdown-button"></button>
<div id="dropdownMenu" class="dropdown-menu" style="display: none;"></div>
</div>
</div>
<div id="responsesContainer" class="responses-container"></div>
<div class="input-container">
<textarea id="promptInput" class="chat-input" placeholder="Enter your prompt for image generation" rows="3"></textarea>
<button id="sendButton" class="send-button">Send</button>
</div>
<div id="errorMessage" class="error-message" style="display: none;"></div>
</div>
</main>
<script type="text/javascript" src="./APIChat.js"></script>
</body>
</html>

BIN
demo/webui/system.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
demo/webui/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB