Spaces:
Sleeping
Sleeping
Update Deployment_UI.py
Browse files- Deployment_UI.py +21 -40
Deployment_UI.py
CHANGED
|
@@ -31,17 +31,14 @@ def deployment_ui_page():
|
|
| 31 |
.thumb { max-width: 420px; border:1px solid #ccc; border-radius:6px; }
|
| 32 |
.meta { font-size:12px; color:#555; margin-top:4px; }
|
| 33 |
.download { display:inline-block; margin-top:6px; }
|
| 34 |
-
|
| 35 |
/* Blob row (new, minimal) */
|
| 36 |
#blob-bar { display:flex; gap:8px; margin:0 0 10px; align-items:center; }
|
| 37 |
#blob-url { flex:1; padding:8px; border:1px solid #ccc; border-radius:8px; }
|
| 38 |
#blob-load, #blob-ingest { padding:8px 12px; border:1px solid #ccc; border-radius:8px; background:#f7f7f7; cursor:pointer; }
|
| 39 |
#blob-load:hover, #blob-ingest:hover { background:#eee; }
|
| 40 |
#blob-status { font-size:12px; color:#555; }
|
| 41 |
-
|
| 42 |
/* Blob preview box styled like logs/results */
|
| 43 |
#blob-preview { border:1px solid #ddd; border-radius:8px; padding:10px; background:#fff; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:12px; max-height:220px; overflow:auto; margin-bottom:10px; }
|
| 44 |
-
|
| 45 |
/* loading mask */
|
| 46 |
#boot-mask{position:fixed;inset:0;background:rgba(255,255,255,.85);display:none;
|
| 47 |
align-items:center;justify-content:center;flex-direction:column;z-index:9999}
|
|
@@ -53,7 +50,6 @@ def deployment_ui_page():
|
|
| 53 |
</head>
|
| 54 |
<body>
|
| 55 |
<a href="/trythis" class="button">← Back</a>
|
| 56 |
-
|
| 57 |
<!-- New: Blob tools row (keeps your visual rhythm) -->
|
| 58 |
<div id="blob-bar">
|
| 59 |
<input id="blob-url" type="text" placeholder="/modelblob.json" />
|
|
@@ -62,7 +58,6 @@ def deployment_ui_page():
|
|
| 62 |
<div id="blob-status"></div>
|
| 63 |
</div>
|
| 64 |
<pre id="blob-preview" aria-live="polite" style="display:none;"></pre>
|
| 65 |
-
|
| 66 |
<div id="chat-window"></div>
|
| 67 |
<div id="input-area">
|
| 68 |
<input type="text" id="user-input" placeholder="Describe an image to generate…" />
|
|
@@ -94,15 +89,13 @@ def deployment_ui_page():
|
|
| 94 |
const chat = document.getElementById('chat-window');
|
| 95 |
const bootMask = document.getElementById('boot-mask');
|
| 96 |
const bootMsg = document.getElementById('boot-msg');
|
| 97 |
-
|
| 98 |
// New blob controls
|
| 99 |
const blobUrl = document.getElementById('blob-url');
|
| 100 |
const blobLoad = document.getElementById('blob-load');
|
| 101 |
const blobIngest = document.getElementById('blob-ingest');
|
| 102 |
const blobStatus = document.getElementById('blob-status');
|
| 103 |
const blobPreview = document.getElementById('blob-preview');
|
| 104 |
-
|
| 105 |
-
let running = false, ready = false;
|
| 106 |
let MODEL_BASE = null, PREDICT_ROUTE = null;
|
| 107 |
|
| 108 |
// Maintain your existing auto-ingest-on-load behavior (unchanged)
|
|
@@ -121,6 +114,7 @@ def deployment_ui_page():
|
|
| 121 |
function showBoot(msg){ bootMsg.textContent = msg || 'Starting…'; bootMask.style.display = 'flex'; }
|
| 122 |
function setBoot(msg){ bootMsg.textContent = msg || 'Working…'; }
|
| 123 |
function hideBoot(){ bootMask.style.display = 'none'; }
|
|
|
|
| 124 |
function log(msg, cls='log-raw') {
|
| 125 |
const line = document.createElement('div');
|
| 126 |
line.className = 'log-line ' + cls;
|
|
@@ -128,6 +122,7 @@ def deployment_ui_page():
|
|
| 128 |
logs.appendChild(line);
|
| 129 |
logs.scrollTop = logs.scrollHeight;
|
| 130 |
}
|
|
|
|
| 131 |
toggleBtn.addEventListener('click', () => {
|
| 132 |
const collapsed = logs.classList.toggle('collapsed');
|
| 133 |
toggleBtn.textContent = collapsed ? "Show Logs" : "Hide Logs";
|
|
@@ -139,6 +134,7 @@ def deployment_ui_page():
|
|
| 139 |
log(text, r.ok ? 'log-ok' : 'log-err');
|
| 140 |
try { return { ok: r.ok, json: JSON.parse(text) }; } catch { return { ok: r.ok, json: { _raw: text } }; }
|
| 141 |
}
|
|
|
|
| 142 |
async function del(path) {
|
| 143 |
const r = await fetch(path, { method: 'DELETE' });
|
| 144 |
const text = await r.text();
|
|
@@ -189,24 +185,28 @@ def deployment_ui_page():
|
|
| 189 |
blobLoad.addEventListener('click', loadBlob);
|
| 190 |
blobIngest.addEventListener('click', ingestBlob);
|
| 191 |
|
| 192 |
-
// create
|
| 193 |
async function begin() {
|
| 194 |
if (running) return;
|
| 195 |
-
running = true;
|
| 196 |
showBoot('Creating instance…');
|
| 197 |
const create = await post('/api/compute/create_instance');
|
| 198 |
-
if (!create.ok) { running = false; hideBoot(); return; }
|
| 199 |
-
const ok = await ensureReady(true);
|
| 200 |
hideBoot();
|
|
|
|
|
|
|
|
|
|
| 201 |
}
|
|
|
|
| 202 |
async function stopOnce() {
|
| 203 |
await del('/api/compute/delete_instance');
|
| 204 |
-
hideBoot(); setBoot('');
|
| 205 |
}
|
|
|
|
| 206 |
async function endAll() {
|
| 207 |
await stopOnce();
|
| 208 |
MODEL_BASE = null; PREDICT_ROUTE = null;
|
| 209 |
}
|
|
|
|
| 210 |
function appendResult(job_id, b64, timings) {
|
| 211 |
const div = document.createElement('div'); div.className = 'result';
|
| 212 |
const img = document.createElement('img'); img.className = 'thumb'; img.src = 'data:image/png;base64,' + b64;
|
|
@@ -214,7 +214,9 @@ def deployment_ui_page():
|
|
| 214 |
const meta = document.createElement('div'); meta.className = 'meta'; meta.textContent = `job ${job_id} | ${JSON.stringify(timings || {})}`;
|
| 215 |
div.append(img, document.createTextNode(' '), a, meta); results.prepend(div);
|
| 216 |
}
|
|
|
|
| 217 |
function escapeHtml(s){ return String(s).replace(/[&<>"']/g, m => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); }
|
|
|
|
| 218 |
function addBlock(sender, html) {
|
| 219 |
const wrap = document.createElement('div');
|
| 220 |
wrap.style.margin = '8px 0';
|
|
@@ -223,8 +225,10 @@ def deployment_ui_page():
|
|
| 223 |
chat.scrollTop = chat.scrollHeight;
|
| 224 |
return wrap;
|
| 225 |
}
|
|
|
|
| 226 |
function addUser(text) { return addBlock('You', `<div>${escapeHtml(text)}</div>`); }
|
| 227 |
function addModel(text) { return addBlock('Model', `<div>${escapeHtml(text)}</div>`); }
|
|
|
|
| 228 |
function addModelImg(b64){
|
| 229 |
const wrap = addBlock('Model', '');
|
| 230 |
const img = document.createElement('img');
|
|
@@ -233,37 +237,13 @@ def deployment_ui_page():
|
|
| 233 |
wrap.lastElementChild.appendChild(img);
|
| 234 |
return wrap;
|
| 235 |
}
|
|
|
|
| 236 |
function addLoader() {
|
| 237 |
const wrap = addBlock('Model', `<div id="spinner" style="display:inline-block">Loading…</div>`);
|
| 238 |
return wrap;
|
| 239 |
}
|
| 240 |
-
function looksBase64(s){ return /^[A-Za-z0-9+/=\\s]+$/.test(s||'') && String(s||'').length > 100; }
|
| 241 |
|
| 242 |
-
|
| 243 |
-
async function ensureReady(verbose=false) {
|
| 244 |
-
for (let i = 0; i < 60; i++) {
|
| 245 |
-
const r = await fetch('/api/compute/wait_instance');
|
| 246 |
-
const j = await r.json();
|
| 247 |
-
const cs = j.cachedState || {};
|
| 248 |
-
const status = (cs.status || '').toUpperCase();
|
| 249 |
-
if (verbose) {
|
| 250 |
-
if (cs.isReady === true) setBoot('Ready');
|
| 251 |
-
else if (status === 'RUNNING' && cs.base) setBoot('Warming model…');
|
| 252 |
-
else setBoot(`Status: ${status || '…'}`);
|
| 253 |
-
}
|
| 254 |
-
if (cs.base && cs.predictRoute && cs.isReady === true) {
|
| 255 |
-
MODEL_BASE = cs.base;
|
| 256 |
-
PREDICT_ROUTE = cs.predictRoute.startsWith('/') ? cs.predictRoute : `/${cs.predictRoute}`;
|
| 257 |
-
log(`PROMPT_ENDPOINT ${MODEL_BASE}${PREDICT_ROUTE}`, 'log-ok');
|
| 258 |
-
ready = true;
|
| 259 |
-
return true;
|
| 260 |
-
}
|
| 261 |
-
log(`READY_POLL base=${cs.base ? 'yes' : 'no'} route=${cs.predictRoute || ''} isReady=${cs.isReady === true} status=${status}`, 'log-raw');
|
| 262 |
-
await new Promise(res => setTimeout(res, 1000));
|
| 263 |
-
}
|
| 264 |
-
ready = false;
|
| 265 |
-
return false;
|
| 266 |
-
}
|
| 267 |
|
| 268 |
// backend hop for prompts
|
| 269 |
async function sendViaBackend(prompt) {
|
|
@@ -276,10 +256,11 @@ def deployment_ui_page():
|
|
| 276 |
log(`POST /api/middleware/infer → ${r.status}`, r.ok ? 'log-ok' : 'log-err');
|
| 277 |
try { return { ok: r.ok, json: JSON.parse(text) }; } catch { return { ok: r.ok, json: { _raw: text } }; }
|
| 278 |
}
|
|
|
|
| 279 |
async function sendMessage() {
|
| 280 |
const prompt = (userInput.value || '').trim();
|
| 281 |
if (!prompt) return;
|
| 282 |
-
|
| 283 |
addUser(prompt);
|
| 284 |
const loader = addLoader();
|
| 285 |
userInput.disabled = true;
|
|
|
|
| 31 |
.thumb { max-width: 420px; border:1px solid #ccc; border-radius:6px; }
|
| 32 |
.meta { font-size:12px; color:#555; margin-top:4px; }
|
| 33 |
.download { display:inline-block; margin-top:6px; }
|
|
|
|
| 34 |
/* Blob row (new, minimal) */
|
| 35 |
#blob-bar { display:flex; gap:8px; margin:0 0 10px; align-items:center; }
|
| 36 |
#blob-url { flex:1; padding:8px; border:1px solid #ccc; border-radius:8px; }
|
| 37 |
#blob-load, #blob-ingest { padding:8px 12px; border:1px solid #ccc; border-radius:8px; background:#f7f7f7; cursor:pointer; }
|
| 38 |
#blob-load:hover, #blob-ingest:hover { background:#eee; }
|
| 39 |
#blob-status { font-size:12px; color:#555; }
|
|
|
|
| 40 |
/* Blob preview box styled like logs/results */
|
| 41 |
#blob-preview { border:1px solid #ddd; border-radius:8px; padding:10px; background:#fff; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:12px; max-height:220px; overflow:auto; margin-bottom:10px; }
|
|
|
|
| 42 |
/* loading mask */
|
| 43 |
#boot-mask{position:fixed;inset:0;background:rgba(255,255,255,.85);display:none;
|
| 44 |
align-items:center;justify-content:center;flex-direction:column;z-index:9999}
|
|
|
|
| 50 |
</head>
|
| 51 |
<body>
|
| 52 |
<a href="/trythis" class="button">← Back</a>
|
|
|
|
| 53 |
<!-- New: Blob tools row (keeps your visual rhythm) -->
|
| 54 |
<div id="blob-bar">
|
| 55 |
<input id="blob-url" type="text" placeholder="/modelblob.json" />
|
|
|
|
| 58 |
<div id="blob-status"></div>
|
| 59 |
</div>
|
| 60 |
<pre id="blob-preview" aria-live="polite" style="display:none;"></pre>
|
|
|
|
| 61 |
<div id="chat-window"></div>
|
| 62 |
<div id="input-area">
|
| 63 |
<input type="text" id="user-input" placeholder="Describe an image to generate…" />
|
|
|
|
| 89 |
const chat = document.getElementById('chat-window');
|
| 90 |
const bootMask = document.getElementById('boot-mask');
|
| 91 |
const bootMsg = document.getElementById('boot-msg');
|
|
|
|
| 92 |
// New blob controls
|
| 93 |
const blobUrl = document.getElementById('blob-url');
|
| 94 |
const blobLoad = document.getElementById('blob-load');
|
| 95 |
const blobIngest = document.getElementById('blob-ingest');
|
| 96 |
const blobStatus = document.getElementById('blob-status');
|
| 97 |
const blobPreview = document.getElementById('blob-preview');
|
| 98 |
+
let running = false;
|
|
|
|
| 99 |
let MODEL_BASE = null, PREDICT_ROUTE = null;
|
| 100 |
|
| 101 |
// Maintain your existing auto-ingest-on-load behavior (unchanged)
|
|
|
|
| 114 |
function showBoot(msg){ bootMsg.textContent = msg || 'Starting…'; bootMask.style.display = 'flex'; }
|
| 115 |
function setBoot(msg){ bootMsg.textContent = msg || 'Working…'; }
|
| 116 |
function hideBoot(){ bootMask.style.display = 'none'; }
|
| 117 |
+
|
| 118 |
function log(msg, cls='log-raw') {
|
| 119 |
const line = document.createElement('div');
|
| 120 |
line.className = 'log-line ' + cls;
|
|
|
|
| 122 |
logs.appendChild(line);
|
| 123 |
logs.scrollTop = logs.scrollHeight;
|
| 124 |
}
|
| 125 |
+
|
| 126 |
toggleBtn.addEventListener('click', () => {
|
| 127 |
const collapsed = logs.classList.toggle('collapsed');
|
| 128 |
toggleBtn.textContent = collapsed ? "Show Logs" : "Hide Logs";
|
|
|
|
| 134 |
log(text, r.ok ? 'log-ok' : 'log-err');
|
| 135 |
try { return { ok: r.ok, json: JSON.parse(text) }; } catch { return { ok: r.ok, json: { _raw: text } }; }
|
| 136 |
}
|
| 137 |
+
|
| 138 |
async function del(path) {
|
| 139 |
const r = await fetch(path, { method: 'DELETE' });
|
| 140 |
const text = await r.text();
|
|
|
|
| 185 |
blobLoad.addEventListener('click', loadBlob);
|
| 186 |
blobIngest.addEventListener('click', ingestBlob);
|
| 187 |
|
| 188 |
+
// create instance (no readiness gating)
|
| 189 |
async function begin() {
|
| 190 |
if (running) return;
|
| 191 |
+
running = true;
|
| 192 |
showBoot('Creating instance…');
|
| 193 |
const create = await post('/api/compute/create_instance');
|
|
|
|
|
|
|
| 194 |
hideBoot();
|
| 195 |
+
if (!create.ok) {
|
| 196 |
+
running = false;
|
| 197 |
+
}
|
| 198 |
}
|
| 199 |
+
|
| 200 |
async function stopOnce() {
|
| 201 |
await del('/api/compute/delete_instance');
|
| 202 |
+
hideBoot(); setBoot(''); running = false;
|
| 203 |
}
|
| 204 |
+
|
| 205 |
async function endAll() {
|
| 206 |
await stopOnce();
|
| 207 |
MODEL_BASE = null; PREDICT_ROUTE = null;
|
| 208 |
}
|
| 209 |
+
|
| 210 |
function appendResult(job_id, b64, timings) {
|
| 211 |
const div = document.createElement('div'); div.className = 'result';
|
| 212 |
const img = document.createElement('img'); img.className = 'thumb'; img.src = 'data:image/png;base64,' + b64;
|
|
|
|
| 214 |
const meta = document.createElement('div'); meta.className = 'meta'; meta.textContent = `job ${job_id} | ${JSON.stringify(timings || {})}`;
|
| 215 |
div.append(img, document.createTextNode(' '), a, meta); results.prepend(div);
|
| 216 |
}
|
| 217 |
+
|
| 218 |
function escapeHtml(s){ return String(s).replace(/[&<>"']/g, m => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); }
|
| 219 |
+
|
| 220 |
function addBlock(sender, html) {
|
| 221 |
const wrap = document.createElement('div');
|
| 222 |
wrap.style.margin = '8px 0';
|
|
|
|
| 225 |
chat.scrollTop = chat.scrollHeight;
|
| 226 |
return wrap;
|
| 227 |
}
|
| 228 |
+
|
| 229 |
function addUser(text) { return addBlock('You', `<div>${escapeHtml(text)}</div>`); }
|
| 230 |
function addModel(text) { return addBlock('Model', `<div>${escapeHtml(text)}</div>`); }
|
| 231 |
+
|
| 232 |
function addModelImg(b64){
|
| 233 |
const wrap = addBlock('Model', '');
|
| 234 |
const img = document.createElement('img');
|
|
|
|
| 237 |
wrap.lastElementChild.appendChild(img);
|
| 238 |
return wrap;
|
| 239 |
}
|
| 240 |
+
|
| 241 |
function addLoader() {
|
| 242 |
const wrap = addBlock('Model', `<div id="spinner" style="display:inline-block">Loading…</div>`);
|
| 243 |
return wrap;
|
| 244 |
}
|
|
|
|
| 245 |
|
| 246 |
+
function looksBase64(s){ return /^[A-Za-z0-9+/=\\s]+$/.test(s||'') && String(s||'').length > 100; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
|
| 248 |
// backend hop for prompts
|
| 249 |
async function sendViaBackend(prompt) {
|
|
|
|
| 256 |
log(`POST /api/middleware/infer → ${r.status}`, r.ok ? 'log-ok' : 'log-err');
|
| 257 |
try { return { ok: r.ok, json: JSON.parse(text) }; } catch { return { ok: r.ok, json: { _raw: text } }; }
|
| 258 |
}
|
| 259 |
+
|
| 260 |
async function sendMessage() {
|
| 261 |
const prompt = (userInput.value || '').trim();
|
| 262 |
if (!prompt) return;
|
| 263 |
+
|
| 264 |
addUser(prompt);
|
| 265 |
const loader = addLoader();
|
| 266 |
userInput.disabled = true;
|