shreyask commited on
Commit
6c01099
·
verified ·
1 Parent(s): ee342db

Upload folder using huggingface_hub

Browse files
assets/index-CENTWb2t.css ADDED
@@ -0,0 +1 @@
 
 
1
+ :root{--bg:#0a0a0a;--bg-2:#141414;--fg:#e8e8e8;--fg-dim:#888;--accent:#fff;--border:#222;--error:#ff6b6b;font-family:JetBrains Mono,SF Mono,Menlo,Consolas,monospace}*{box-sizing:border-box}body{background:var(--bg);color:var(--fg);min-height:100vh;margin:0}.mono{font-family:inherit}.topbar{border-bottom:1px solid var(--border);padding:16px 24px}.brand-name{font-weight:600}.brand-sub{color:var(--fg-dim);margin-left:8px}.grid{grid-template-columns:1fr 1.4fr;gap:16px;min-height:calc(100vh - 60px);padding:16px 24px;display:grid}.pane{flex-direction:column;gap:8px;display:flex}.pane-header{color:var(--fg-dim);justify-content:space-between;align-items:baseline;font-size:13px;display:flex}.model-indicator,.loading-indicator{color:var(--fg-dim);font-size:12px}textarea#tools{background:var(--bg-2);min-height:400px;color:var(--fg);border:1px solid var(--border);resize:none;border-radius:4px;flex:1;padding:12px;font-size:13px}input#query{background:var(--bg-2);color:var(--fg);border:1px solid var(--border);border-radius:4px;padding:10px 12px;font-size:14px}.result-pane{background:var(--bg-2);border:1px solid var(--border);white-space:pre-wrap;word-break:break-word;border-radius:4px;flex:1;margin:0;padding:12px;font-size:13px;overflow:auto}.result-pane.error{color:var(--error)}.examples{flex-wrap:wrap;gap:8px;display:flex}.example-chip{color:var(--fg);border:1px solid var(--border);cursor:pointer;background:0 0;border-radius:999px;padding:4px 12px;font-size:12px;transition:opacity .12s ease-in-out}.example-chip:hover:not(:disabled){background:var(--bg-2)}.example-chip:disabled,input#query:disabled,textarea#tools:disabled{opacity:.4;cursor:not-allowed}.loading-indicator.busy{animation:1.2s ease-in-out infinite pulse}@keyframes pulse{0%,to{opacity:.55}50%{opacity:1}}.error-inline{color:var(--error);margin-top:4px;font-size:12px}
assets/index-Dh-E6L2Y.js ADDED
The diff for this file is too large to render. See raw diff
 
index.html CHANGED
@@ -4,9 +4,9 @@
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>Needle Playground (Browser)</title>
7
- <script type="module" crossorigin src="/assets/index-BJo2pRXI.js"></script>
8
  <link rel="modulepreload" crossorigin href="/assets/chunk-jRWAZmH_.js">
9
- <link rel="stylesheet" crossorigin href="/assets/index-CRllq4cA.css">
10
  </head>
11
  <body>
12
  <header class="topbar">
 
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>Needle Playground (Browser)</title>
7
+ <script type="module" crossorigin src="/assets/index-Dh-E6L2Y.js"></script>
8
  <link rel="modulepreload" crossorigin href="/assets/chunk-jRWAZmH_.js">
9
+ <link rel="stylesheet" crossorigin href="/assets/index-CENTWb2t.css">
10
  </head>
11
  <body>
12
  <header class="topbar">
src/main.ts CHANGED
@@ -4,7 +4,7 @@ import type { Tokenizer } from './tokenizer';
4
  import { loadSessions } from './runtime';
5
  import type { NeedleSessions } from './runtime';
6
  import { generate } from './generate';
7
- import { mountUI, setStatus, renderResult, renderError, readTools } from './ui';
8
  import type { UI } from './ui';
9
  import { TOKENIZER_URL, SPECIALS_URL } from './config';
10
 
@@ -25,14 +25,17 @@ async function fetchJson<T>(url: string): Promise<T> {
25
  async function boot() {
26
  const ui = mountUI();
27
  try {
28
- setStatus(ui, 'loading model…');
 
29
  const [sessions, tokenizerBytes, specials] = await Promise.all([
30
- loadSessions(m => setStatus(ui, m)),
31
  fetchBytes(TOKENIZER_URL),
32
  fetchJson<Specials>(SPECIALS_URL),
33
  ]);
34
  const tokenizer = await createTokenizer(tokenizerBytes);
35
- setStatus(ui, 'ready');
 
 
36
  wireRun(ui, sessions, tokenizer, specials);
37
  } catch (e) {
38
  setStatus(ui, 'failed');
@@ -49,7 +52,12 @@ function wireRun(ui: UI, sessions: NeedleSessions, tokenizer: Tokenizer, special
49
  const query = ui.queryEl.value.trim();
50
  if (!query) return;
51
  running = true;
52
- setStatus(ui, 'generating…');
 
 
 
 
 
53
  try {
54
  const result = await generate(
55
  sessions, tokenizer, query, tools.tools,
@@ -60,14 +68,20 @@ function wireRun(ui: UI, sessions: NeedleSessions, tokenizer: Tokenizer, special
60
  maxNewTokens: 256,
61
  },
62
  (_id, decodedSoFar) => {
 
63
  let display = decodedSoFar;
64
  if (display.startsWith('<tool_call>')) display = display.slice('<tool_call>'.length);
65
  renderResult(ui, display);
66
  },
67
  );
 
 
 
 
68
  renderResult(ui, result.text);
69
- setStatus(ui, 'ready');
70
  } catch (e) {
 
71
  renderError(ui, `Generation failed: ${(e as Error).message}`);
72
  setStatus(ui, 'ready');
73
  } finally {
 
4
  import { loadSessions } from './runtime';
5
  import type { NeedleSessions } from './runtime';
6
  import { generate } from './generate';
7
+ import { mountUI, setStatus, renderResult, renderError, readTools, setInteractiveEnabled } from './ui';
8
  import type { UI } from './ui';
9
  import { TOKENIZER_URL, SPECIALS_URL } from './config';
10
 
 
25
  async function boot() {
26
  const ui = mountUI();
27
  try {
28
+ setStatus(ui, 'loading model…', true);
29
+ const t0 = performance.now();
30
  const [sessions, tokenizerBytes, specials] = await Promise.all([
31
+ loadSessions(m => setStatus(ui, m, true)),
32
  fetchBytes(TOKENIZER_URL),
33
  fetchJson<Specials>(SPECIALS_URL),
34
  ]);
35
  const tokenizer = await createTokenizer(tokenizerBytes);
36
+ const loadSecs = ((performance.now() - t0) / 1000).toFixed(1);
37
+ setStatus(ui, `ready · loaded in ${loadSecs}s`);
38
+ setInteractiveEnabled(ui, true);
39
  wireRun(ui, sessions, tokenizer, specials);
40
  } catch (e) {
41
  setStatus(ui, 'failed');
 
52
  const query = ui.queryEl.value.trim();
53
  if (!query) return;
54
  running = true;
55
+ let tokensSoFar = 0;
56
+ const t0 = performance.now();
57
+ const tick = setInterval(() => {
58
+ const elapsed = ((performance.now() - t0) / 1000).toFixed(1);
59
+ setStatus(ui, `generating… ${elapsed}s · ${tokensSoFar} tok`, true);
60
+ }, 100);
61
  try {
62
  const result = await generate(
63
  sessions, tokenizer, query, tools.tools,
 
68
  maxNewTokens: 256,
69
  },
70
  (_id, decodedSoFar) => {
71
+ tokensSoFar += 1;
72
  let display = decodedSoFar;
73
  if (display.startsWith('<tool_call>')) display = display.slice('<tool_call>'.length);
74
  renderResult(ui, display);
75
  },
76
  );
77
+ clearInterval(tick);
78
+ const elapsedMs = performance.now() - t0;
79
+ const elapsed = (elapsedMs / 1000).toFixed(2);
80
+ const tps = (result.ids.length / (elapsedMs / 1000)).toFixed(1);
81
  renderResult(ui, result.text);
82
+ setStatus(ui, `ready · ${elapsed}s · ${result.ids.length} tok · ${tps} tok/s`);
83
  } catch (e) {
84
+ clearInterval(tick);
85
  renderError(ui, `Generation failed: ${(e as Error).message}`);
86
  setStatus(ui, 'ready');
87
  } finally {
src/style.css CHANGED
@@ -118,12 +118,29 @@ input#query {
118
  padding: 4px 12px;
119
  font-size: 12px;
120
  cursor: pointer;
 
121
  }
122
 
123
- .example-chip:hover {
124
  background: var(--bg-2);
125
  }
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  .error-inline {
128
  color: var(--error);
129
  font-size: 12px;
 
118
  padding: 4px 12px;
119
  font-size: 12px;
120
  cursor: pointer;
121
+ transition: opacity 120ms ease-in-out;
122
  }
123
 
124
+ .example-chip:hover:not(:disabled) {
125
  background: var(--bg-2);
126
  }
127
 
128
+ .example-chip:disabled,
129
+ input#query:disabled,
130
+ textarea#tools:disabled {
131
+ opacity: 0.4;
132
+ cursor: not-allowed;
133
+ }
134
+
135
+ /* Subtle pulse on the status text while model is loading or generating. */
136
+ .loading-indicator.busy {
137
+ animation: pulse 1.2s ease-in-out infinite;
138
+ }
139
+ @keyframes pulse {
140
+ 0%, 100% { opacity: 0.55; }
141
+ 50% { opacity: 1; }
142
+ }
143
+
144
  .error-inline {
145
  color: var(--error);
146
  font-size: 12px;
src/ui.ts CHANGED
@@ -19,11 +19,13 @@ export function mountUI(): UI {
19
  // Pre-populate tools textarea with DEFAULT_TOOLS
20
  toolsEl.value = JSON.stringify(DEFAULT_TOOLS, null, 2);
21
 
22
- // Render example chips
 
23
  EXAMPLES.forEach((ex: Example) => {
24
  const chip = document.createElement('button');
25
  chip.className = 'example-chip';
26
  chip.textContent = ex.label;
 
27
 
28
  chip.addEventListener('click', () => {
29
  // Set query value from example
@@ -52,8 +54,20 @@ export function mountUI(): UI {
52
  };
53
  }
54
 
55
- export function setStatus(ui: UI, msg: string): void {
56
  ui.statusEl.textContent = msg;
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
  export function renderResult(ui: UI, rawText: string): void {
 
19
  // Pre-populate tools textarea with DEFAULT_TOOLS
20
  toolsEl.value = JSON.stringify(DEFAULT_TOOLS, null, 2);
21
 
22
+ // Render example chips — disabled until the model finishes loading
23
+ queryEl.disabled = true;
24
  EXAMPLES.forEach((ex: Example) => {
25
  const chip = document.createElement('button');
26
  chip.className = 'example-chip';
27
  chip.textContent = ex.label;
28
+ chip.disabled = true;
29
 
30
  chip.addEventListener('click', () => {
31
  // Set query value from example
 
54
  };
55
  }
56
 
57
+ export function setStatus(ui: UI, msg: string, busy = false): void {
58
  ui.statusEl.textContent = msg;
59
+ ui.statusEl.classList.toggle('busy', busy);
60
+ }
61
+
62
+ /**
63
+ * Toggle the disabled state of the example chips and the query input.
64
+ * Chips start disabled at mount; main.ts flips them on once the model finishes
65
+ * loading. Toggling off again during a busy / failed state is also valid.
66
+ */
67
+ export function setInteractiveEnabled(ui: UI, enabled: boolean): void {
68
+ ui.queryEl.disabled = !enabled;
69
+ ui.examplesEl.querySelectorAll<HTMLButtonElement>('button.example-chip')
70
+ .forEach(btn => { btn.disabled = !enabled; });
71
  }
72
 
73
  export function renderResult(ui: UI, rawText: string): void {