PeterPinetree commited on
Commit
8eb8955
·
1 Parent(s): c749a1d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +180 -17
index.html CHANGED
@@ -1,19 +1,182 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Next Token Predictor</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root{
11
+ --bg:#0b0f14; --text:#ffffff; --muted:#9aa4b2; --accent:#38bdf8; --border:#1f2a3a;
12
+ --chip:#111827; --chip-border:#263246; --chip-hover:#1a2434;
13
+ --mono:'JetBrains Mono',ui-monospace,Menlo,Consolas,monospace; --sans:Inter,system-ui,-apple-system,"Segoe UI",Roboto,Ubuntu,"Helvetica Neue",Arial;
14
+ }
15
+ *{box-sizing:border-box} body{margin:0;background:radial-gradient(900px 500px at 10% -10%, #07314a, transparent),var(--bg);color:var(--text);font-family:var(--sans)}
16
+ .wrap{max-width:1100px;margin:0 auto;padding:16px}
17
+ h1{margin:.2rem 0 .25rem;font-size:1.9rem;color:var(--accent)}
18
+ .sub{color:var(--muted);margin:0 0 .8rem}
19
+ .row{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap}
20
+ .card{background:linear-gradient(180deg,#0c1624,#0a1220);border:1px solid var(--border);border-radius:14px;padding:12px}
21
+ .grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}
22
+ @media (max-width:900px){.grid{grid-template-columns:1fr}}
23
+ select,input{border-radius:10px;border:1px solid var(--border);background:#0a1220;color:var(--text);padding:.6rem .8rem;outline:none}
24
+ select:focus,input:focus{border-color:var(--accent)}
25
+ #status{color:var(--muted);font-size:.9rem}
26
+ .tokens{display:flex;gap:.4rem;flex-wrap:wrap}
27
+ .chip{border:1px solid var(--chip-border);background:var(--chip);padding:.35rem .5rem;border-radius:10px;font-family:var(--mono);font-size:.9rem}
28
+ .chip.special{border-color:var(--accent);background:#0b2235}
29
+ .ids{font-family:var(--mono);font-size:.85rem;color:#c7d2fe}
30
+ .topk{display:flex;gap:.4rem;flex-wrap:wrap}
31
+ .k{padding:.35rem .5rem;border-radius:10px;background:#102133;border:1px solid #1c2b44;font-family:var(--mono);cursor:pointer}
32
+ .k:hover{border-color:var(--accent)}
33
+ .note{color:var(--muted);font-size:.8rem}
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <main class="wrap">
38
+ <h1>Next Token Predictor</h1>
39
+ <div class="sub">How an LLM guesses the next token — runs entirely in your browser.</div>
40
+
41
+ <section class="card" style="margin-bottom:12px">
42
+ <div class="row">
43
+ <label>Model&nbsp;
44
+ <select id="model">
45
+ <option value="Xenova/distilgpt2">distilgpt2 (fast)</option>
46
+ <option value="Xenova/gpt2">gpt2</option>
47
+ </select>
48
+ </label>
49
+ <input id="text" type="text" value="Never gonna give you up, never gonna let you" style="flex:1" />
50
+ <span id="status">Loading model…</span>
51
+ </div>
52
+ </section>
53
+
54
+ <section class="grid">
55
+ <article class="card">
56
+ <h3 style="margin:.2rem 0 .4rem">Context</h3>
57
+ <div id="tokens" class="tokens"></div>
58
+ <div id="ids" class="ids" style="margin-top:.4rem"></div>
59
+ <div class="note" style="margin-top:.4rem">Tokens are subword pieces. IDs are how the model “sees” them. Special tokens are highlighted.</div>
60
+ </article>
61
+
62
+ <article class="card">
63
+ <h3 style="margin:.2rem 0 .4rem">Top-10 next tokens</h3>
64
+ <div id="topk" class="topk"></div>
65
+ <div class="note" style="margin-top:.4rem">Click a candidate to append it and see the next step.</div>
66
+ </article>
67
+ </section>
68
+
69
+ <p class="note" style="margin-top:10px">First load may take a few seconds while the model downloads and warms up.</p>
70
+ </main>
71
+
72
+ <!-- Option A: Load from CDN (simple). If it’s blocked on your network, comment this out and use Option B below. -->
73
+ <script type="module">
74
+ const tf = await import('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2');
75
+ tf.env.useBrowserCache = true;
76
+ tf.env.allowRemoteModels = true; // load from Hub/CDN
77
+ tf.env.allowLocalModels = false; // flip to true only if you host weights yourself
78
+
79
+ // ----- UI -----
80
+ const $ = s => document.querySelector(s);
81
+ const modelSel = $('#model'), textIn = $('#text'), statusEl = $('#status');
82
+ const tokensEl = $('#tokens'), idsEl = $('#ids'), topkEl = $('#topk');
83
+
84
+ let tokenizer = null, model = null, warm = false;
85
+
86
+ function status(m){ statusEl.textContent = m; }
87
+
88
+ async function load(modelId){
89
+ status(`Loading ${modelId}…`);
90
+ tokenizer = await tf.AutoTokenizer.from_pretrained(modelId);
91
+ // Use int8 quant if available to keep VRAM/CPU low
92
+ model = await tf.AutoModelForCausalLM.from_pretrained(modelId, { dtype: 'q8' }).catch(async ()=>{
93
+ return await tf.AutoModelForCausalLM.from_pretrained(modelId);
94
+ });
95
+ warm = false;
96
+ status('Model ready.');
97
+ await predict(); // run once so the screen isn’t empty
98
+ }
99
+
100
+ // Small helpers
101
+ function softmax(arr){
102
+ const max = Math.max(...arr);
103
+ const exps = arr.map(v => Math.exp(v - max));
104
+ const sum = exps.reduce((a,b)=>a+b,0);
105
+ return exps.map(v => v/sum);
106
+ }
107
+ function topK(arr, k){
108
+ const idx = arr.map((p,i)=>[p,i]).sort((a,b)=>b[0]-a[0]).slice(0,k).map(x=>x[1]);
109
+ return idx.map(i => ({ i, p: arr[i] }));
110
+ }
111
+
112
+ async function predict(appendId=null){
113
+ if (!model || !tokenizer) return;
114
+
115
+ let text = textIn.value ?? '';
116
+ if (appendId !== null){
117
+ // Append a predicted token ID to the current context
118
+ text = tokenizer.decode([...tokenizer.encode(text), appendId], { skip_special_tokens:false, clean_up_tokenization_spaces:false });
119
+ textIn.value = text;
120
+ }
121
+
122
+ status(warm ? 'Predicting…' : 'Warming up…');
123
+
124
+ // Encode to IDs (array)
125
+ const ids = await tokenizer.encode(text);
126
+ // Render context tokens/ids (including specials)
127
+ const specials = new Set(tokenizer.all_special_ids || []);
128
+ const ctxTokens = (typeof tokenizer.convert_ids_to_tokens === 'function')
129
+ ? tokenizer.convert_ids_to_tokens(ids)
130
+ : ids.map(id => tokenizer.decode([id], { skip_special_tokens:false, clean_up_tokenization_spaces:false }));
131
+
132
+ tokensEl.innerHTML = ctxTokens.map((t,idx)=>{
133
+ const isSpecial = specials.has(ids[idx]);
134
+ return `<span class="chip${isSpecial?' special':''}">${escapeHtml(t)}</span>`;
135
+ }).join('');
136
+ idsEl.textContent = ids.join(' ');
137
+
138
+ // Run single forward to get logits at last position
139
+ const inputs = { input_ids: tf.Tensor.from(ids, { dtype: 'int64', shape: [1, ids.length] }) };
140
+ const out = await model(inputs);
141
+ const lastLogits = Array.from(out.logits.data.slice((ids.length-1)*out.logits.dims[2], ids.length*out.logits.dims[2]));
142
+ const probs = softmax(lastLogits);
143
+ const k = topK(probs, 10);
144
+
145
+ // Render top-k bar
146
+ topkEl.innerHTML = '';
147
+ for (const { i, p } of k){
148
+ const tok = (typeof tokenizer.id_to_token === 'function')
149
+ ? tokenizer.id_to_token(i)
150
+ : tokenizer.decode([i], { skip_special_tokens:false, clean_up_tokenization_spaces:false });
151
+ const btn = document.createElement('button');
152
+ btn.className = 'k';
153
+ btn.textContent = `${tok} ${(p*100).toFixed(1)}%`;
154
+ btn.title = `id: ${i}`;
155
+ btn.onclick = ()=>predict(i);
156
+ topkEl.appendChild(btn);
157
+ }
158
+
159
+ warm = true;
160
+ status('Ready.');
161
+ }
162
+
163
+ function escapeHtml(s){ return String(s).replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m])); }
164
+
165
+ // Events
166
+ textIn.addEventListener('input', ()=>predict());
167
+ modelSel.addEventListener('change', ()=>load(modelSel.value));
168
+
169
+ // Initial load
170
+ await load(modelSel.value);
171
+ </script>
172
+
173
+ <!-- Option B: if your CDN path is blocked, upload transformers.min.js to /assets/vendor/ and use:
174
+ <script type="module">
175
+ const tf = await import('./assets/vendor/transformers.min.js');
176
+ tf.env.useBrowserCache = true;
177
+ tf.env.allowRemoteModels = true;
178
+ // (rest identical)
179
+ </script>
180
+ -->
181
+ </body>
182
  </html>