DibaAi commited on
Commit
58dac6f
·
verified ·
1 Parent(s): 3e1aadf

UI: direct Gradio SSE adapter, bidi lists, no page overflow

Browse files
Files changed (2) hide show
  1. diba-ui.css +1 -1
  2. diba-ui.js +31 -10
diba-ui.css CHANGED
@@ -10,7 +10,7 @@
10
  --accent: #4CB58C; --accent-ink: #0B1511; --accent-soft: #1E3A2F; --danger: #F2B8B5; --shadow: 0 6px 24px rgba(0, 0, 0, .35);
11
  }
12
 
13
- html, body { height: 100%; }
14
  body.diba-lock { overflow: hidden !important; }
15
  #diba-root, #diba-root * { box-sizing: border-box; }
16
  #diba-root {
 
10
  --accent: #4CB58C; --accent-ink: #0B1511; --accent-soft: #1E3A2F; --danger: #F2B8B5; --shadow: 0 6px 24px rgba(0, 0, 0, .35);
11
  }
12
 
13
+ html, body { height: 100%; margin: 0; }
14
  body.diba-lock { overflow: hidden !important; }
15
  #diba-root, #diba-root * { box-sizing: border-box; }
16
  #diba-root {
diba-ui.js CHANGED
@@ -155,16 +155,35 @@
155
  }
156
  }
157
 
158
- let gradioClient = null;
 
159
  async function* streamGradio(messages) {
160
- if (!gradioClient) { const mod = await import(CDN.gradio); gradioClient = await mod.Client.connect(window.location.origin + window.location.pathname.replace(/\/$/, "")); }
161
- const job = gradioClient.submit("/chat_stream", [JSON.stringify(messages.slice(-16)), S.settings.temperature, S.settings.max_tokens]);
162
- S.controller.signal.addEventListener("abort", () => { try { job.cancel(); } catch (e) { /* already done */ } });
163
- for await (const m of job) {
164
- if (S.controller.signal.aborted) return;
165
- if (m.type === "data" && m.data && typeof m.data[0] === "string") yield m.data[0];
166
- if (m.type === "status" && m.stage === "error") throw new Error(m.message || "error");
167
- if (m.type === "status" && m.stage === "complete") return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
  }
170
 
@@ -284,7 +303,7 @@
284
  async function runAssistant() {
285
  const c = current(); if (!c) return;
286
  const history = c.messages.filter((m) => !m.error && m.content).map((m) => ({ role: m.role, content: m.content }));
287
- const bot = { role: "assistant", content: "", pending: true, cold: BACKEND === "gradio" && !gradioClient };
288
  c.messages.push(bot); renderThread(); toBottom(true);
289
  S.streaming = true; S.controller = new AbortController(); setSendState();
290
  let frame = 0; let latest = "";
@@ -376,6 +395,8 @@
376
  applyChrome(); toBottom(true); setSendState();
377
  Promise.all([loadScript(CDN.marked), loadScript(CDN.purify), loadScript(CDN.hljs)]).then(() => { renderThread(); toBottom(true); }).catch(() => { /* plain text fallback */ });
378
  if (window.innerWidth > 820) el.input.focus();
 
 
379
  }
380
  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot); else boot();
381
  })();
 
155
  }
156
  }
157
 
158
+ // Gradio REST streaming protocol: POST /gradio_api/call/<api> -> {event_id}; GET .../<event_id> -> SSE (generating|complete|error)
159
+ let gradioWarm = false;
160
  async function* streamGradio(messages) {
161
+ const base = window.location.origin + window.location.pathname.replace(/\/+$/, "");
162
+ const signal = S.controller.signal;
163
+ const start = await fetch(base + "/gradio_api/call/chat_stream", {
164
+ method: "POST", headers: { "Content-Type": "application/json" }, signal,
165
+ body: JSON.stringify({ data: [JSON.stringify(messages.slice(-16)), S.settings.temperature, S.settings.max_tokens] }),
166
+ });
167
+ if (!start.ok) throw new Error("HTTP " + start.status);
168
+ const { event_id } = await start.json();
169
+ const res = await fetch(base + "/gradio_api/call/chat_stream/" + event_id, { signal });
170
+ if (!res.ok || !res.body) throw new Error("HTTP " + res.status);
171
+ const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = ""; let event = "";
172
+ for (;;) {
173
+ const { value, done } = await reader.read(); if (done) return;
174
+ buf += dec.decode(value, { stream: true });
175
+ let i;
176
+ while ((i = buf.indexOf("\n")) >= 0) {
177
+ const line = buf.slice(0, i).replace(/\r$/, ""); buf = buf.slice(i + 1);
178
+ if (line.startsWith("event:")) { event = line.slice(6).trim(); continue; }
179
+ if (!line.startsWith("data:")) continue;
180
+ const raw = line.slice(5).trim();
181
+ if (event === "error") throw new Error(raw || "gradio error");
182
+ if (event === "generating" || event === "complete") {
183
+ try { const arr = JSON.parse(raw); if (Array.isArray(arr) && typeof arr[0] === "string") { gradioWarm = true; yield arr[0]; } } catch (e) { /* heartbeat */ }
184
+ if (event === "complete") return;
185
+ }
186
+ }
187
  }
188
  }
189
 
 
303
  async function runAssistant() {
304
  const c = current(); if (!c) return;
305
  const history = c.messages.filter((m) => !m.error && m.content).map((m) => ({ role: m.role, content: m.content }));
306
+ const bot = { role: "assistant", content: "", pending: true, cold: BACKEND === "gradio" && !gradioWarm };
307
  c.messages.push(bot); renderThread(); toBottom(true);
308
  S.streaming = true; S.controller = new AbortController(); setSendState();
309
  let frame = 0; let latest = "";
 
395
  applyChrome(); toBottom(true); setSendState();
396
  Promise.all([loadScript(CDN.marked), loadScript(CDN.purify), loadScript(CDN.hljs)]).then(() => { renderThread(); toBottom(true); }).catch(() => { /* plain text fallback */ });
397
  if (window.innerWidth > 820) el.input.focus();
398
+ const autoQ = qp.get("q"); // test hook: ?q=... sends one message on load
399
+ if (autoQ && location.hash !== "#demo") setTimeout(() => { newChat(); sendMessage(autoQ); }, 1200);
400
  }
401
  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot); else boot();
402
  })();