Spaces:
Running
Running
pack precision map
Browse files
tools.js
CHANGED
|
@@ -1,124 +1,151 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* The five tools, declared once.
|
| 3 |
-
*
|
| 4 |
-
* Each Space linked to the others from its own footer, which meant the
|
| 5 |
-
* same five URLs written out in five files — twenty-five places for a
|
| 6 |
-
* rename to break, and a Space rename is invisible from the linking
|
| 7 |
-
* side: the link keeps returning a page, and the page is a 404.
|
| 8 |
-
*
|
| 9 |
-
* So the registry lives here and is copied into each Space at deploy
|
| 10 |
-
* time, next to `progress.js`. One file to change when a tool moves.
|
| 11 |
-
*
|
| 12 |
-
* The order is the order somebody actually needs them: find a model,
|
| 13 |
-
* see how much context it can serve, learn how to run it, then check
|
| 14 |
-
* what the pack really contains before spending the download.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
export const TOOLS = {
|
| 18 |
-
search: {
|
| 19 |
-
space: 'model-search-that-answers',
|
| 20 |
-
title: 'Find a model that fits',
|
| 21 |
-
asks: 'which models fit my card at all',
|
| 22 |
-
// What the visitor has just learned, and why the next tool follows.
|
| 23 |
-
leadsTo: 'context',
|
| 24 |
-
takes: 'query',
|
| 25 |
-
},
|
| 26 |
-
context: {
|
| 27 |
-
space: 'what-context-fits',
|
| 28 |
-
title: 'What context will actually fit?',
|
| 29 |
-
asks: 'how long a conversation this one can serve',
|
| 30 |
-
leadsTo: 'parser',
|
| 31 |
-
takes: 'repo',
|
| 32 |
-
},
|
| 33 |
-
parser: {
|
| 34 |
-
space: 'reasoning-parser-advisor',
|
| 35 |
-
title: 'Does this model need a reasoning parser?',
|
| 36 |
-
asks: 'how to serve it without silently empty answers',
|
| 37 |
-
leadsTo: 'precision',
|
| 38 |
-
takes: 'repo',
|
| 39 |
-
},
|
| 40 |
-
precision: {
|
| 41 |
-
space: 'pack-precision-map',
|
| 42 |
-
title: 'How much of this pack is actually 4-bit?',
|
| 43 |
-
asks: 'what a quantized pack really compressed, and what it kept',
|
| 44 |
-
leadsTo: 'integrity',
|
| 45 |
-
takes: 'repo',
|
| 46 |
-
},
|
| 47 |
-
integrity: {
|
| 48 |
-
space: 'pack-integrity-check',
|
| 49 |
-
title: 'Does this pack keep what it claims?',
|
| 50 |
-
asks: 'whether anything was dropped or left undeclared',
|
| 51 |
-
leadsTo: null,
|
| 52 |
-
takes: 'repo',
|
| 53 |
-
},
|
| 54 |
-
};
|
| 55 |
-
|
| 56 |
-
export const ORDER = ['search', 'context', 'parser', 'precision', 'integrity'];
|
| 57 |
-
|
| 58 |
-
const BASE = 'https://huggingface.co/spaces/aleada';
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
*
|
| 63 |
-
*
|
| 64 |
-
*
|
| 65 |
-
*
|
| 66 |
-
*
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
*
|
| 89 |
-
*
|
| 90 |
-
*
|
| 91 |
-
*
|
| 92 |
-
*
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
/
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
.
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* The five tools, declared once.
|
| 3 |
+
*
|
| 4 |
+
* Each Space linked to the others from its own footer, which meant the
|
| 5 |
+
* same five URLs written out in five files — twenty-five places for a
|
| 6 |
+
* rename to break, and a Space rename is invisible from the linking
|
| 7 |
+
* side: the link keeps returning a page, and the page is a 404.
|
| 8 |
+
*
|
| 9 |
+
* So the registry lives here and is copied into each Space at deploy
|
| 10 |
+
* time, next to `progress.js`. One file to change when a tool moves.
|
| 11 |
+
*
|
| 12 |
+
* The order is the order somebody actually needs them: find a model,
|
| 13 |
+
* see how much context it can serve, learn how to run it, then check
|
| 14 |
+
* what the pack really contains before spending the download.
|
| 15 |
+
*/
|
| 16 |
+
|
| 17 |
+
export const TOOLS = {
|
| 18 |
+
search: {
|
| 19 |
+
space: 'model-search-that-answers',
|
| 20 |
+
title: 'Find a model that fits',
|
| 21 |
+
asks: 'which models fit my card at all',
|
| 22 |
+
// What the visitor has just learned, and why the next tool follows.
|
| 23 |
+
leadsTo: 'context',
|
| 24 |
+
takes: 'query',
|
| 25 |
+
},
|
| 26 |
+
context: {
|
| 27 |
+
space: 'what-context-fits',
|
| 28 |
+
title: 'What context will actually fit?',
|
| 29 |
+
asks: 'how long a conversation this one can serve',
|
| 30 |
+
leadsTo: 'parser',
|
| 31 |
+
takes: 'repo',
|
| 32 |
+
},
|
| 33 |
+
parser: {
|
| 34 |
+
space: 'reasoning-parser-advisor',
|
| 35 |
+
title: 'Does this model need a reasoning parser?',
|
| 36 |
+
asks: 'how to serve it without silently empty answers',
|
| 37 |
+
leadsTo: 'precision',
|
| 38 |
+
takes: 'repo',
|
| 39 |
+
},
|
| 40 |
+
precision: {
|
| 41 |
+
space: 'pack-precision-map',
|
| 42 |
+
title: 'How much of this pack is actually 4-bit?',
|
| 43 |
+
asks: 'what a quantized pack really compressed, and what it kept',
|
| 44 |
+
leadsTo: 'integrity',
|
| 45 |
+
takes: 'repo',
|
| 46 |
+
},
|
| 47 |
+
integrity: {
|
| 48 |
+
space: 'pack-integrity-check',
|
| 49 |
+
title: 'Does this pack keep what it claims?',
|
| 50 |
+
asks: 'whether anything was dropped or left undeclared',
|
| 51 |
+
leadsTo: null,
|
| 52 |
+
takes: 'repo',
|
| 53 |
+
},
|
| 54 |
+
};
|
| 55 |
+
|
| 56 |
+
export const ORDER = ['search', 'context', 'parser', 'precision', 'integrity'];
|
| 57 |
+
|
| 58 |
+
const BASE = 'https://huggingface.co/spaces/aleada';
|
| 59 |
+
const HUB = `${BASE}/start-here`;
|
| 60 |
+
|
| 61 |
+
/**
|
| 62 |
+
* A link to one tool, carrying the model with it.
|
| 63 |
+
*
|
| 64 |
+
* A funnel where each step starts empty is five separate tools with
|
| 65 |
+
* links between them. `?repo=` is read on load by every page that takes
|
| 66 |
+
* one, so clicking through keeps the model you were looking at.
|
| 67 |
+
*/
|
| 68 |
+
export function url(key, repo = null) {
|
| 69 |
+
const t = TOOLS[key];
|
| 70 |
+
if (!t) return BASE;
|
| 71 |
+
const q = repo && t.takes === 'repo'
|
| 72 |
+
? `?repo=${encodeURIComponent(repo)}`
|
| 73 |
+
: repo && t.takes === 'query'
|
| 74 |
+
? `?q=${encodeURIComponent(repo)}` : '';
|
| 75 |
+
return `${BASE}/${t.space}${q}`;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
/** The model this page was opened with, if a link carried one over. */
|
| 79 |
+
export function repoFromUrl() {
|
| 80 |
+
const p = new URLSearchParams(window.location.search);
|
| 81 |
+
return (p.get('repo') || p.get('q') || '').trim() || null;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
|
| 85 |
+
({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
| 86 |
+
|
| 87 |
+
/**
|
| 88 |
+
* The next questions, rendered under a result.
|
| 89 |
+
*
|
| 90 |
+
* The whole chain is rendered, not just the parts that come after: the
|
| 91 |
+
* current step is marked so a visitor who arrived from a link can see
|
| 92 |
+
* where they are, and the steps behind stay reachable because the order
|
| 93 |
+
* is a suggestion and not a gate.
|
| 94 |
+
*
|
| 95 |
+
* Offered only once there is an answer on screen — a next step shown
|
| 96 |
+
* before the current one is finished is clutter, and one that cannot
|
| 97 |
+
* carry the model over is a link to an empty form.
|
| 98 |
+
*/
|
| 99 |
+
export function nextSteps(fromKey, repo = null) {
|
| 100 |
+
const here = ORDER.indexOf(fromKey);
|
| 101 |
+
const items = ORDER.map((k, i) => {
|
| 102 |
+
const t = TOOLS[k];
|
| 103 |
+
if (i === here) {
|
| 104 |
+
// The step you are on, shown rather than hidden. A list of "other
|
| 105 |
+
// tools" tells you what exists; a chain with your place marked
|
| 106 |
+
// tells you how far along you are, which is the thing somebody
|
| 107 |
+
// arriving from a link actually wants to know.
|
| 108 |
+
return `<li class="ns-here"><span class="ns-mark">✓</span>
|
| 109 |
+
<span class="ns-title">${esc(t.title)}</span>
|
| 110 |
+
<span class="ns-why">you are here</span></li>`;
|
| 111 |
+
}
|
| 112 |
+
const ahead = i > here;
|
| 113 |
+
return `<li class="${ahead ? 'ns-next' : 'ns-back'}">
|
| 114 |
+
<span class="ns-mark">${ahead ? '→' : '·'}</span>
|
| 115 |
+
<a href="${url(k, repo)}" target="_blank" rel="noopener">${esc(t.title)}</a>
|
| 116 |
+
<span class="ns-why">${esc(t.asks)}</span></li>`;
|
| 117 |
+
}).join('');
|
| 118 |
+
|
| 119 |
+
return `<nav class="nextsteps" aria-label="The rest of the chain">
|
| 120 |
+
<h4>Your next question${repo ? ` for <code>${esc(repo)}</code>` : ''}</h4>
|
| 121 |
+
<ul>${items}</ul>
|
| 122 |
+
<a class="ns-hub" href="${HUB}${repo ? `?repo=${encodeURIComponent(repo)}` : ''}"
|
| 123 |
+
target="_blank" rel="noopener">All five, and why they are in this order →</a>
|
| 124 |
+
</nav>`;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
/** Styles for the block above, injected once so each Space need not carry them. */
|
| 128 |
+
export function ensureNextStepStyles() {
|
| 129 |
+
if (document.getElementById('ns-style')) return;
|
| 130 |
+
const el = document.createElement('style');
|
| 131 |
+
el.id = 'ns-style';
|
| 132 |
+
el.textContent = `
|
| 133 |
+
.nextsteps { margin: 1.6rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
|
| 134 |
+
.nextsteps h4 { margin: 0 0 .5rem; font-size: .74rem; font-weight: 600;
|
| 135 |
+
text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
|
| 136 |
+
.nextsteps h4 code { text-transform: none; letter-spacing: 0; }
|
| 137 |
+
.nextsteps ul { list-style: none; margin: 0; padding: 0; }
|
| 138 |
+
.nextsteps li { margin: .32rem 0; font-size: .89rem; display: flex;
|
| 139 |
+
gap: .5rem; align-items: baseline; }
|
| 140 |
+
.ns-mark { font-family: var(--mono); font-size: .8rem; color: var(--muted);
|
| 141 |
+
width: 1rem; flex: 0 0 1rem; }
|
| 142 |
+
.ns-here .ns-mark { color: var(--accent); }
|
| 143 |
+
.ns-here .ns-title { font-weight: 600; }
|
| 144 |
+
.ns-back { opacity: .72; }
|
| 145 |
+
.ns-hub { display: inline-block; margin-top: .7rem; font-size: .84rem; }
|
| 146 |
+
.nextsteps a { color: var(--accent); text-decoration: none; }
|
| 147 |
+
.nextsteps a:hover { text-decoration: underline; }
|
| 148 |
+
.ns-why { color: var(--muted); }
|
| 149 |
+
.ns-why::before { content: " — "; }`;
|
| 150 |
+
document.head.appendChild(el);
|
| 151 |
+
}
|