mirror of https://github.com/corca-ai/EVAL
Feature/gui (#24)
* feat: simple gui * fix: prevent deadlock * fix: show answer * refactor: change endpoint * fix: set random session id by default * fix: dashboard wip * fix: link-body-emphasis * fix: path problem when using local file * fix: handle path problem * fix: import error * fix: files regex * fix: prompt about output files * fix: show response files * fix: handle errorpull/26/head
parent
30595bc9c6
commit
a15ba07eca
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>EVAL {% block title %}{% endblock %}</title>
|
||||||
|
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet" />
|
||||||
|
<link
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
{% block head %} {% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header
|
||||||
|
class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none"
|
||||||
|
>
|
||||||
|
<svg class="bi me-2" width="40" height="32">
|
||||||
|
<use xlink:href="#bootstrap"></use>
|
||||||
|
</svg>
|
||||||
|
<span class="fs-4">EVAL</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
href="https://github.com/corca-ai/EVAL"
|
||||||
|
class="nav-link"
|
||||||
|
target="_blank"
|
||||||
|
>Github</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<div
|
||||||
|
class="d-flex flex-column flex-shrink-0 p-3 bg-body-tertiary"
|
||||||
|
style="width: 280px; height: 80vh"
|
||||||
|
>
|
||||||
|
<ul id="nav-sidebar" class="nav nav-pills flex-column mb-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/" class="nav-link link-body-emphasis">
|
||||||
|
<svg class="bi pe-none me-2" width="16" height="16">
|
||||||
|
<use xlink:href="#home"></use>
|
||||||
|
</svg>
|
||||||
|
Execute
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/dashboard" class="nav-link link-body-emphasis">
|
||||||
|
<svg class="bi pe-none me-2" width="16" height="16">
|
||||||
|
<use xlink:href="#speedometer2"></use>
|
||||||
|
</svg>
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-100">
|
||||||
|
<div class="container">{% block content %}{% endblock %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
<script src="{{ url_for('static', path='/layout.js') }}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,3 @@
|
|||||||
|
{% extends "base.html" %} {% block head %} {% endblock %} {% block content %}
|
||||||
|
<div class="p-3">Work in progress.</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,36 @@
|
|||||||
|
{% extends "base.html" %} {% block head %}
|
||||||
|
<script src="{{ url_for('static', path='/execute.js') }}"></script>
|
||||||
|
{% endblock %} {% block content %}
|
||||||
|
<div class="container-fluid pb-3">
|
||||||
|
<div class="d-grid gap-3" style="grid-template-columns: 2fr 3fr">
|
||||||
|
<div class="bg-body-tertiary border rounded-3 p-3">
|
||||||
|
<div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="prompt" class="form-label">Prompt</label>
|
||||||
|
<textarea id="prompt" name="prompt" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="files" class="form-label">Files</label>
|
||||||
|
<input id="files" type="file" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="session" class="form-label">Session</label>
|
||||||
|
<input id="session" name="session" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" onclick="submit(event)">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-body-tertiary border rounded-3 p-3">
|
||||||
|
<div>
|
||||||
|
<div id="answer"></div>
|
||||||
|
<div id="response-files"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
setRandomSessionId();
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -1,26 +1,28 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
from env import DotEnv
|
from env import DotEnv
|
||||||
|
|
||||||
from .base import AbstractUploader
|
from .base import AbstractUploader
|
||||||
|
|
||||||
|
|
||||||
class StaticUploader(AbstractUploader):
|
class StaticUploader(AbstractUploader):
|
||||||
STATIC_DIR = "static"
|
def __init__(self, server: str, path: Path, endpoint: str):
|
||||||
|
|
||||||
def __init__(self, server: str):
|
|
||||||
self.server = server
|
self.server = server
|
||||||
|
self.path = path
|
||||||
|
self.endpoint = endpoint
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_settings(settings: DotEnv) -> "StaticUploader":
|
def from_settings(settings: DotEnv, path: Path, endpoint: str) -> "StaticUploader":
|
||||||
return StaticUploader(settings["SERVER"])
|
return StaticUploader(settings["SERVER"], path, endpoint)
|
||||||
|
|
||||||
def get_url(self, uploaded_path: str) -> str:
|
def get_url(self, uploaded_path: str) -> str:
|
||||||
return f"{self.server}/{uploaded_path}"
|
return f"{self.server}/{uploaded_path}"
|
||||||
|
|
||||||
def upload(self, filepath: str):
|
def upload(self, filepath: str):
|
||||||
upload_path = os.path.join(StaticUploader.STATIC_DIR, filepath)
|
relative_path = Path("generated") / filepath.split("/")[-1]
|
||||||
os.makedirs(os.path.dirname(upload_path), exist_ok=True)
|
file_path = self.path / relative_path
|
||||||
shutil.copy(filepath, upload_path)
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||||
return f"{self.server}/{upload_path}"
|
shutil.copy(filepath, file_path)
|
||||||
|
endpoint_path = self.endpoint / relative_path
|
||||||
|
return f"{self.server}/{endpoint_path}"
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
const setAnswer = (answer, files) => {
|
||||||
|
document.getElementById("answer").textContent = answer;
|
||||||
|
const filesDiv = document.getElementById("response-files");
|
||||||
|
filesDiv.innerHTML = "";
|
||||||
|
files.forEach((file) => {
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.classList.add("icon-link");
|
||||||
|
a.href = file;
|
||||||
|
a.textContent = file.split("/").pop();
|
||||||
|
a.setAttribute("download", "");
|
||||||
|
filesDiv.appendChild(a);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
setAnswer("Loading...", []);
|
||||||
|
const files = [];
|
||||||
|
const rawfiles = document.getElementById("files").files;
|
||||||
|
|
||||||
|
if (rawfiles.length > 0) {
|
||||||
|
const formData = new FormData();
|
||||||
|
for (let i = 0; i < rawfiles.length; i++) {
|
||||||
|
formData.append("files", rawfiles[i]);
|
||||||
|
}
|
||||||
|
const respone = await fetch("/upload", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
const { urls } = await respone.json();
|
||||||
|
files.push(...urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = document.getElementById("prompt").value;
|
||||||
|
const session = document.getElementById("session").value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/execute", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt,
|
||||||
|
session,
|
||||||
|
files,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(await response.text());
|
||||||
|
}
|
||||||
|
const { answer, files: responseFiles } = await response.json();
|
||||||
|
setAnswer(answer, responseFiles);
|
||||||
|
} catch (e) {
|
||||||
|
setAnswer("Error: " + e.message, []);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setRandomSessionId = () => {
|
||||||
|
const sessionId = Math.random().toString(36).substring(2, 15);
|
||||||
|
document.getElementById("session").value = sessionId;
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
const highlightActiveNavItem = () => {
|
||||||
|
const navItems = document.querySelectorAll("#nav-sidebar > li > a");
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
navItems.forEach((item) => {
|
||||||
|
if (item.getAttribute("href") === currentPath) {
|
||||||
|
item.classList.add("active");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
highlightActiveNavItem();
|
Loading…
Reference in New Issue