Upload folder using huggingface_hub
Browse files- model.py +1 -1
- static/brand.css +58 -36
- static/index.html +7 -17
- static/strip.js +5 -4
model.py
CHANGED
|
@@ -18,7 +18,7 @@ DEMO = {
|
|
| 18 |
"title": "Sentiment and role, <em>per span</em>",
|
| 19 |
"subtitle": "Document classification labels the whole review. GLiNER 2.5 attaches <strong>attributes to every entity</strong> it extracts.",
|
| 20 |
"model_name": "GLiNER 2.5",
|
| 21 |
-
"model_variant": "0.3B parameters
|
| 22 |
"code": (
|
| 23 |
'schema = (model.create_schema()\n'
|
| 24 |
' .entities({"product": "A product mentioned in the text",\n'
|
|
|
|
| 18 |
"title": "Sentiment and role, <em>per span</em>",
|
| 19 |
"subtitle": "Document classification labels the whole review. GLiNER 2.5 attaches <strong>attributes to every entity</strong> it extracts.",
|
| 20 |
"model_name": "GLiNER 2.5",
|
| 21 |
+
"model_variant": "0.3B parameters, f32, CPU",
|
| 22 |
"code": (
|
| 23 |
'schema = (model.create_schema()\n'
|
| 24 |
' .entities({"product": "A product mentioned in the text",\n'
|
static/brand.css
CHANGED
|
@@ -61,31 +61,39 @@ body {
|
|
| 61 |
/* ---- model strip ---- */
|
| 62 |
.model-strip {
|
| 63 |
display: flex; align-items: center; gap: 14px;
|
| 64 |
-
margin: 18px 0 20px; padding:
|
| 65 |
-
background:
|
| 66 |
-
}
|
| 67 |
-
.model-glyph {
|
| 68 |
-
width: 30px; height: 30px; border-radius: var(--radius-s); flex-shrink: 0;
|
| 69 |
-
background: var(--ink);
|
| 70 |
-
display: grid; place-items: center;
|
| 71 |
}
|
| 72 |
-
.model-
|
| 73 |
-
.model-name-row
|
|
|
|
| 74 |
.model-name-row a:hover { border-bottom-color: var(--orange-deep); color: var(--orange-deep); }
|
| 75 |
-
.model-variant {
|
| 76 |
-
|
| 77 |
-
.
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
.
|
| 86 |
-
.
|
| 87 |
-
.
|
| 88 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
/* ---- workspace ---- */
|
| 91 |
.workspace { display: grid; grid-template-columns: 5fr 7fr; gap: 16px; align-items: stretch; }
|
|
@@ -109,23 +117,37 @@ textarea, .editor {
|
|
| 109 |
textarea:focus, .editor:focus { outline: 2px solid var(--ash); outline-offset: -2px; }
|
| 110 |
textarea:focus-visible { outline: 2px solid var(--orange-deep); }
|
| 111 |
|
| 112 |
-
/* ---- example
|
| 113 |
-
.examples-panel { margin: 0 0
|
| 114 |
.examples-label {
|
| 115 |
font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
|
| 116 |
-
color: var(--ink-faint); display: block; margin-bottom:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
}
|
| 118 |
-
.example-list { display: flex; flex-wrap: wrap; gap: 8px; }
|
| 119 |
.ex {
|
| 120 |
-
font: inherit;
|
| 121 |
-
|
| 122 |
-
padding:
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
.ex:
|
| 128 |
-
.ex
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
/* ---- output body ---- */
|
| 131 |
.output-body {
|
|
|
|
| 61 |
/* ---- model strip ---- */
|
| 62 |
.model-strip {
|
| 63 |
display: flex; align-items: center; gap: 14px;
|
| 64 |
+
margin: 18px 0 20px; padding: 12px 16px;
|
| 65 |
+
background: transparent; border: 0; border-bottom: 1px solid var(--ash); border-radius: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
}
|
| 67 |
+
.model-copy { flex: 1; min-width: 0; }
|
| 68 |
+
.model-name-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
|
| 69 |
+
.model-name-row a { color: var(--ink); font-weight: 600; text-decoration: none; font-size: 15px; border-bottom: 1px solid transparent; }
|
| 70 |
.model-name-row a:hover { border-bottom-color: var(--orange-deep); color: var(--orange-deep); }
|
| 71 |
+
.model-variant { font-size: 12px; color: var(--ink-faint); font-family: var(--font-mono); }
|
| 72 |
+
|
| 73 |
+
.status-pill {
|
| 74 |
+
display: inline-flex; align-items: center; gap: 7px;
|
| 75 |
+
font-family: var(--font-mono); font-size: 11px; font-weight: 500;
|
| 76 |
+
color: var(--ink-soft); white-space: nowrap;
|
| 77 |
+
padding: 3px 9px; border: 1px solid var(--ash); border-radius: 4px;
|
| 78 |
+
background: var(--greige);
|
| 79 |
+
}
|
| 80 |
+
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--warm-gray); flex-shrink: 0; }
|
| 81 |
+
.status-pill.is-ready { border-color: var(--ok-bg); color: var(--ok); }
|
| 82 |
+
.status-pill.is-ready .status-dot { background: var(--ok); }
|
| 83 |
+
.status-pill.is-error { border-color: rgba(168,69,69,0.3); color: var(--neg); }
|
| 84 |
+
.status-pill.is-error .status-dot { background: var(--neg); }
|
| 85 |
+
.status-pill.is-loading .status-dot { background: var(--orange); animation: pulse-dot 1.1s ease-in-out infinite; }
|
| 86 |
+
@keyframes pulse-dot { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
|
| 87 |
+
|
| 88 |
+
.load-block { min-width: 180px; }
|
| 89 |
+
.load-track { height: 3px; background: transparent; border-radius: 99px; overflow: hidden; position: relative; }
|
| 90 |
+
.load-track::before { content: ""; position: absolute; inset: 0; background: var(--ash); border-radius: 99px; opacity: 0; transition: opacity 200ms; }
|
| 91 |
+
.model-strip.is-loading .load-track::before, .model-strip.is-preparing .load-track::before { opacity: 1; }
|
| 92 |
+
#load-progress { display: block; height: 100%; width: 0; background: var(--orange); border-radius: 99px; transition: width 400ms var(--ease-out); position: relative; }
|
| 93 |
+
.model-strip.is-ready #load-progress { width: 0; opacity: 0; }
|
| 94 |
+
.model-strip.is-ready #load-percent { display: none; }
|
| 95 |
+
|
| 96 |
+
/* hide percent counter entirely; status pill communicates state */
|
| 97 |
|
| 98 |
/* ---- workspace ---- */
|
| 99 |
.workspace { display: grid; grid-template-columns: 5fr 7fr; gap: 16px; align-items: stretch; }
|
|
|
|
| 117 |
textarea:focus, .editor:focus { outline: 2px solid var(--ash); outline-offset: -2px; }
|
| 118 |
textarea:focus-visible { outline: 2px solid var(--orange-deep); }
|
| 119 |
|
| 120 |
+
/* ---- example selector: indexed action rows (no pill chips) ---- */
|
| 121 |
+
.examples-panel { margin: 0 0 16px; }
|
| 122 |
.examples-label {
|
| 123 |
font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
|
| 124 |
+
color: var(--ink-faint); display: block; margin-bottom: 10px;
|
| 125 |
+
}
|
| 126 |
+
.example-list {
|
| 127 |
+
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 128 |
+
border-top: 1px solid var(--ash); border-bottom: 1px solid var(--ash);
|
| 129 |
}
|
|
|
|
| 130 |
.ex {
|
| 131 |
+
font: inherit; text-align: left; background: transparent; border: 0;
|
| 132 |
+
border-right: 1px solid var(--ash);
|
| 133 |
+
padding: 12px 14px 11px; cursor: pointer;
|
| 134 |
+
display: flex; flex-direction: column; gap: 4px;
|
| 135 |
+
transition: background 140ms var(--ease-out);
|
| 136 |
+
position: relative;
|
| 137 |
+
}
|
| 138 |
+
.ex:last-child { border-right: 0; }
|
| 139 |
+
.ex:hover { background: var(--greige); }
|
| 140 |
+
.ex:active { transform: none; }
|
| 141 |
+
.ex:focus-visible { outline: 2px solid var(--orange-deep); outline-offset: -2px; z-index: 1; }
|
| 142 |
+
.ex .ex-idx { font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); }
|
| 143 |
+
.ex .ex-name { font-size: 13px; font-weight: 500; color: var(--ink); }
|
| 144 |
+
.ex.active { background: var(--greige); }
|
| 145 |
+
.ex.active .ex-idx { color: var(--orange-deep); }
|
| 146 |
+
.ex.active::after {
|
| 147 |
+
content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px;
|
| 148 |
+
background: var(--orange-deep);
|
| 149 |
+
}
|
| 150 |
+
@media (max-width: 860px) { .example-list { grid-template-columns: 1fr 1fr; } }
|
| 151 |
|
| 152 |
/* ---- output body ---- */
|
| 153 |
.output-body {
|
static/index.html
CHANGED
|
@@ -18,27 +18,18 @@
|
|
| 18 |
</header>
|
| 19 |
|
| 20 |
<section class="model-strip is-loading" id="model-strip" aria-label="Model status">
|
| 21 |
-
<span class="model-glyph" aria-hidden="true">
|
| 22 |
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
| 23 |
-
<circle cx="5.5" cy="12" r="2.2"/><circle cx="18.5" cy="5" r="2.2"/><circle cx="18.5" cy="19" r="2.2"/>
|
| 24 |
-
<path d="M7.3 10.8 16.6 6M7.3 13.2 16.6 18"/>
|
| 25 |
-
</svg>
|
| 26 |
-
</span>
|
| 27 |
<div class="model-copy">
|
| 28 |
<div class="model-name-row">
|
| 29 |
<a id="model-link" href="#" target="_blank" rel="noopener"></a>
|
| 30 |
<span class="model-variant" id="model-variant"></span>
|
| 31 |
</div>
|
| 32 |
</div>
|
| 33 |
-
<
|
| 34 |
-
<span
|
| 35 |
-
|
|
|
|
| 36 |
<div class="load-block">
|
| 37 |
-
<div class="load-
|
| 38 |
-
<span id="status" role="status" aria-live="polite">Connecting...</span>
|
| 39 |
-
<span id="load-percent">0%</span>
|
| 40 |
-
</div>
|
| 41 |
-
<div class="load-track"><span id="load-progress"></span></div>
|
| 42 |
</div>
|
| 43 |
</section>
|
| 44 |
|
|
@@ -221,7 +212,7 @@ $("output").addEventListener("click", (e) => {
|
|
| 221 |
cfg.examples.forEach((ex, i) => {
|
| 222 |
const b = document.createElement("button");
|
| 223 |
b.type = "button"; b.className = "ex" + (i === 0 ? " active" : "");
|
| 224 |
-
b.
|
| 225 |
b.addEventListener("click", () => {
|
| 226 |
document.querySelectorAll(".ex").forEach((x) => x.classList.toggle("active", x === b));
|
| 227 |
$("input").value = ex.text;
|
|
@@ -233,8 +224,7 @@ $("output").addEventListener("click", (e) => {
|
|
| 233 |
setEmpty("idle");
|
| 234 |
if (cfg.examples[0]) $("input").value = cfg.examples[0].text;
|
| 235 |
READY = await waitForServer();
|
| 236 |
-
stripSet(READY ? "Model ready" : "Model unavailable", READY ? "ready" : "error", 100);
|
| 237 |
-
$("strip-state").textContent = READY ? "Ready" : "Unavailable";
|
| 238 |
if (READY) infer();
|
| 239 |
})();
|
| 240 |
</script>
|
|
|
|
| 18 |
</header>
|
| 19 |
|
| 20 |
<section class="model-strip is-loading" id="model-strip" aria-label="Model status">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
<div class="model-copy">
|
| 22 |
<div class="model-name-row">
|
| 23 |
<a id="model-link" href="#" target="_blank" rel="noopener"></a>
|
| 24 |
<span class="model-variant" id="model-variant"></span>
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
+
<span class="status-pill is-loading" id="status-pill">
|
| 28 |
+
<span class="status-dot" aria-hidden="true"></span>
|
| 29 |
+
<span id="status" role="status" aria-live="polite">Connecting</span>
|
| 30 |
+
</span>
|
| 31 |
<div class="load-block">
|
| 32 |
+
<div class="load-track" aria-hidden="true"><span id="load-progress"></span></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
</div>
|
| 34 |
</section>
|
| 35 |
|
|
|
|
| 212 |
cfg.examples.forEach((ex, i) => {
|
| 213 |
const b = document.createElement("button");
|
| 214 |
b.type = "button"; b.className = "ex" + (i === 0 ? " active" : "");
|
| 215 |
+
b.innerHTML = `<span class="ex-idx">${String(i + 1).padStart(2, "0")}</span><span class="ex-name">${esc(ex.chip)}</span>`;
|
| 216 |
b.addEventListener("click", () => {
|
| 217 |
document.querySelectorAll(".ex").forEach((x) => x.classList.toggle("active", x === b));
|
| 218 |
$("input").value = ex.text;
|
|
|
|
| 224 |
setEmpty("idle");
|
| 225 |
if (cfg.examples[0]) $("input").value = cfg.examples[0].text;
|
| 226 |
READY = await waitForServer();
|
| 227 |
+
stripSet(READY ? "Model ready" : "Model unavailable", READY ? "ready" : "error", READY ? 100 : 0);
|
|
|
|
| 228 |
if (READY) infer();
|
| 229 |
})();
|
| 230 |
</script>
|
static/strip.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
// strip.js
|
| 2 |
// 1. __sign shim: private HF Spaces sign the iframe URL; Safari/incognito block
|
| 3 |
// the third-party cookie, so carry the token on same-origin API requests.
|
| 4 |
// 2. readiness poll: drives the model strip (loading -> preparing -> ready).
|
|
@@ -22,22 +22,23 @@
|
|
| 22 |
|
| 23 |
function stripSet(message, tone, pct) {
|
| 24 |
const strip = document.getElementById("model-strip");
|
|
|
|
| 25 |
document.getElementById("status").textContent = message;
|
| 26 |
strip.className = `model-strip is-${tone}`;
|
|
|
|
| 27 |
if (pct != null) {
|
| 28 |
document.getElementById("load-progress").style.width = `${pct}%`;
|
| 29 |
-
document.getElementById("load-percent").textContent = `${Math.round(pct)}%`;
|
| 30 |
}
|
| 31 |
}
|
| 32 |
|
| 33 |
async function waitForServer() {
|
| 34 |
-
stripSet("Connecting
|
| 35 |
for (let attempt = 0; attempt < 300; attempt++) {
|
| 36 |
try {
|
| 37 |
const r = await fetch("api/ready", { cache: "no-store" });
|
| 38 |
if (r.ok && (await r.json()).ready) return true;
|
| 39 |
} catch (e) { /* still starting */ }
|
| 40 |
-
stripSet("Warming up
|
| 41 |
await new Promise((r) => setTimeout(r, 1000));
|
| 42 |
}
|
| 43 |
return false;
|
|
|
|
| 1 |
+
// strip.js - shared plumbing for every GLiNER 2.5 demo page.
|
| 2 |
// 1. __sign shim: private HF Spaces sign the iframe URL; Safari/incognito block
|
| 3 |
// the third-party cookie, so carry the token on same-origin API requests.
|
| 4 |
// 2. readiness poll: drives the model strip (loading -> preparing -> ready).
|
|
|
|
| 22 |
|
| 23 |
function stripSet(message, tone, pct) {
|
| 24 |
const strip = document.getElementById("model-strip");
|
| 25 |
+
const pill = document.getElementById("status-pill");
|
| 26 |
document.getElementById("status").textContent = message;
|
| 27 |
strip.className = `model-strip is-${tone}`;
|
| 28 |
+
if (pill) pill.className = `status-pill is-${tone}`;
|
| 29 |
if (pct != null) {
|
| 30 |
document.getElementById("load-progress").style.width = `${pct}%`;
|
|
|
|
| 31 |
}
|
| 32 |
}
|
| 33 |
|
| 34 |
async function waitForServer() {
|
| 35 |
+
stripSet("Connecting", "loading", 12);
|
| 36 |
for (let attempt = 0; attempt < 300; attempt++) {
|
| 37 |
try {
|
| 38 |
const r = await fetch("api/ready", { cache: "no-store" });
|
| 39 |
if (r.ok && (await r.json()).ready) return true;
|
| 40 |
} catch (e) { /* still starting */ }
|
| 41 |
+
stripSet("Warming up", "loading", Math.min(92, 12 + attempt * 4));
|
| 42 |
await new Promise((r) => setTimeout(r, 1000));
|
| 43 |
}
|
| 44 |
return false;
|