lvwerra HF Staff commited on
Commit
b164948
·
verified ·
1 Parent(s): 493666d

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. app.py +14 -0
  2. static/index.html +99 -1
app.py CHANGED
@@ -256,6 +256,20 @@ async def wiki_merges() -> Any:
256
  return await _proxy_backend_get("/v1/wiki/merges")
257
 
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  # ──────────────────────────────────────────────────────────────
260
  # OAuth (HF Spaces auto-injects OAUTH_CLIENT_ID/SECRET when
261
  # `hf_oauth: true` is set in README.md).
 
256
  return await _proxy_backend_get("/v1/wiki/merges")
257
 
258
 
259
+ # Trace & stats sharing (proxied same-origin like the wiki reads). The backend
260
+ # computes the project token aggregate and the trace listing; the SPA can't call
261
+ # it cross-origin (no CORS), so the dashboard relays it here.
262
+ @app.get("/api/stats")
263
+ async def stats_proxy() -> Any:
264
+ return await _proxy_backend_get("/v1/stats")
265
+
266
+
267
+ @app.get("/api/traces")
268
+ async def traces_proxy(request: Request) -> Any:
269
+ qs = request.url.query
270
+ return await _proxy_backend_get(f"/v1/traces?{qs}" if qs else "/v1/traces")
271
+
272
+
273
  # ──────────────────────────────────────────────────────────────
274
  # OAuth (HF Spaces auto-injects OAUTH_CLIENT_ID/SECRET when
275
  # `hf_oauth: true` is set in README.md).
static/index.html CHANGED
@@ -258,6 +258,22 @@
258
  .lb-table.wiki-table th, .lb-table.wiki-table td { padding: 8px 8px; }
259
  .lb-table.wiki-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
260
  .lb-table.wiki-table a.lb-link { margin: 0; } /* pill fits its narrow column */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  .lb-table tr { border-bottom: 1px solid var(--border-soft); }
262
  .lb-table tbody tr:last-child { border-bottom: none; }
263
  .lb-table tbody tr:hover td { background: #fafafa; }
@@ -3186,7 +3202,16 @@ function initWiki() {
3186
  <div><table class="lb-table wiki-table"><thead><tr>
3187
  <th style="width:40px">PR</th><th>Title</th><th style="width:108px">Agent</th>
3188
  <th style="width:70px">Kind</th><th style="width:100px">Date</th><th style="width:60px">Link</th>
3189
- </tr></thead><tbody id="mpBody"></tbody></table></div>`;
 
 
 
 
 
 
 
 
 
3190
  refreshWiki();
3191
  }
3192
 
@@ -3197,6 +3222,7 @@ async function refreshWiki() {
3197
  try { const r = await fetchWithTimeout('/api/wiki/leaderboard'); if (r.ok) renderWikiLeaderboard(await r.json()); } catch {}
3198
  try { const r = await fetchWithTimeout('/api/wiki/activity'); if (r.ok) renderActivity(await r.json()); } catch {}
3199
  try { const r = await fetchWithTimeout('/api/wiki/merges'); if (r.ok) renderMergedPRs(await r.json(), ds); } catch {}
 
3200
  }
3201
 
3202
  function fmtMergeDate(ts) {
@@ -3217,6 +3243,78 @@ function renderMergedPRs(data, ds) {
3217
  }).join('') || '<tr><td colspan="6" style="color:#999">No merged PRs yet.</td></tr>';
3218
  }
3219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3220
  let wikiCharts = {};
3221
  // epoch ms -> "MM-DD HH:MM" (UTC) for the time axis ticks
3222
  function fmtTick(v) {
 
258
  .lb-table.wiki-table th, .lb-table.wiki-table td { padding: 8px 8px; }
259
  .lb-table.wiki-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
260
  .lb-table.wiki-table a.lb-link { margin: 0; } /* pill fits its narrow column */
261
+ /* Traces — project token estimate + shared session library */
262
+ .stats-tile {
263
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
264
+ border: 1px solid var(--border); background: #fff;
265
+ padding: 12px 14px; margin-bottom: 12px;
266
+ font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums;
267
+ }
268
+ .stats-tile .big { font-size: 20px; font-weight: 600; color: var(--accent); }
269
+ .stats-tile .muted2 { color: var(--muted-3); }
270
+ .stats-tile .row { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 8px; }
271
+ .stats-tile .row .k {
272
+ color: var(--muted-3); text-transform: uppercase;
273
+ letter-spacing: 0.5px; margin-right: 2px;
274
+ }
275
+ .stats-tile .coverage { color: var(--muted-3); font-size: 10px; margin-top: 8px; }
276
+ .traces-table .muted2 { color: var(--muted-4); }
277
  .lb-table tr { border-bottom: 1px solid var(--border-soft); }
278
  .lb-table tbody tr:last-child { border-bottom: none; }
279
  .lb-table tbody tr:hover td { background: #fafafa; }
 
3202
  <div><table class="lb-table wiki-table"><thead><tr>
3203
  <th style="width:40px">PR</th><th>Title</th><th style="width:108px">Agent</th>
3204
  <th style="width:70px">Kind</th><th style="width:100px">Date</th><th style="width:60px">Link</th>
3205
+ </tr></thead><tbody id="mpBody"></tbody></table></div>
3206
+ <div class="section-title" id="tracesSectionTitle" hidden>Traces<span class="hint" id="tracesHint"></span></div>
3207
+ <div class="stats-tile" id="tracesStatsTile" hidden></div>
3208
+ <div id="tracesListWrap" style="overflow-x:auto" hidden>
3209
+ <table class="lb-table traces-table" style="min-width:760px"><thead><tr>
3210
+ <th style="width:130px">Agent</th><th style="width:90px">Harness</th><th style="width:120px">Model</th>
3211
+ <th class="num" style="width:80px">Tokens</th><th class="num" style="width:56px">Tools</th>
3212
+ <th>Summary</th><th style="width:72px">Trace</th>
3213
+ </tr></thead><tbody id="tracesBody"></tbody></table>
3214
+ </div>`;
3215
  refreshWiki();
3216
  }
3217
 
 
3222
  try { const r = await fetchWithTimeout('/api/wiki/leaderboard'); if (r.ok) renderWikiLeaderboard(await r.json()); } catch {}
3223
  try { const r = await fetchWithTimeout('/api/wiki/activity'); if (r.ok) renderActivity(await r.json()); } catch {}
3224
  try { const r = await fetchWithTimeout('/api/wiki/merges'); if (r.ok) renderMergedPRs(await r.json(), ds); } catch {}
3225
+ refreshTraces();
3226
  }
3227
 
3228
  function fmtMergeDate(ts) {
 
3243
  }).join('') || '<tr><td colspan="6" style="color:#999">No merged PRs yet.</td></tr>';
3244
  }
3245
 
3246
+ // ── Traces: project token estimate + shared-session library (best-effort;
3247
+ // the section stays hidden until ≥1 trace exists) ──
3248
+ const STATS_URL = '/api/stats';
3249
+ const TRACES_URL = '/api/traces?expand=true&limit=50';
3250
+
3251
+ function fmtTokens(n) {
3252
+ if (n == null) return '—';
3253
+ if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e7 ? 0 : 1) + 'M';
3254
+ if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + 'k';
3255
+ return String(n);
3256
+ }
3257
+
3258
+ function renderStatsTile(stats) {
3259
+ const el = document.getElementById('tracesStatsTile');
3260
+ const t = stats.tokens || {};
3261
+ const cost = stats.cost_usd != null ? `<span><span class="k">cost</span> $${stats.cost_usd.toFixed(2)}</span>` : '';
3262
+ const cov = stats.sessions_missing_tokens
3263
+ ? `<div class="coverage">reported floor — ${stats.sessions_missing_tokens} session(s) without token data excluded</div>` : '';
3264
+ el.innerHTML =
3265
+ `<span class="big">${fmtTokens(t.total || 0)}</span> tokens reported`
3266
+ + ` <span class="muted2">· ${stats.sessions_counted} sessions · ${stats.agents_reporting} agents</span>`
3267
+ + `<div class="row"><span><span class="k">in</span> ${fmtTokens(t.input)}</span>`
3268
+ + `<span><span class="k">out</span> ${fmtTokens(t.output)}</span>`
3269
+ + `<span><span class="k">cache</span> ${fmtTokens(t.cache_read)}</span>${cost}</div>${cov}`;
3270
+ }
3271
+
3272
+ function renderTracesList(items) {
3273
+ const body = document.getElementById('tracesBody');
3274
+ body.innerHTML = items.map(it => {
3275
+ const traceFile = it.primary_log_file || '';
3276
+ const full = it.share === 'full' && CFG.bucket_web_url && traceFile;
3277
+ const link = full
3278
+ ? `<a class="lb-link" href="${escapeHtml(CFG.bucket_web_url + '/' + traceFile)}" target="_blank" rel="noopener noreferrer" title="Open the native session log in Hugging Face's trace viewer">view ↗</a>`
3279
+ : '<span class="muted2">stats</span>';
3280
+ return `<tr>
3281
+ <td>${renderAgentName(it.agent)}</td>
3282
+ <td>${escapeHtml(it.harness || '—')}</td>
3283
+ <td>${escapeHtml(it.model || '—')}</td>
3284
+ <td class="num">${fmtTokens(it.total_tokens)}</td>
3285
+ <td class="num">${it.tool_calls == null ? '—' : it.tool_calls}</td>
3286
+ <td class="desc" title="${escapeHtml(it.summary_excerpt || '')}">${escapeHtml(it.summary_excerpt || '')}</td>
3287
+ <td class="links">${link}</td>
3288
+ </tr>`;
3289
+ }).join('');
3290
+ }
3291
+
3292
+ async function refreshTraces() {
3293
+ const titleEl = document.getElementById('tracesSectionTitle');
3294
+ const tileEl = document.getElementById('tracesStatsTile');
3295
+ const wrapEl = document.getElementById('tracesListWrap');
3296
+ const hintEl = document.getElementById('tracesHint');
3297
+ if (!titleEl) return;
3298
+ try {
3299
+ const [sr, tr] = await Promise.allSettled([
3300
+ fetchWithTimeout(STATS_URL), fetchWithTimeout(TRACES_URL),
3301
+ ]);
3302
+ let stats = null, items = [], count = 0;
3303
+ if (sr.status === 'fulfilled' && sr.value.ok) stats = await sr.value.json();
3304
+ if (tr.status === 'fulfilled' && tr.value.ok) {
3305
+ const j = await tr.value.json();
3306
+ items = j.items || []; count = j.count ?? items.length;
3307
+ }
3308
+ const has = items.length > 0 || (stats && (stats.sessions_counted || stats.sessions_missing_tokens));
3309
+ titleEl.hidden = tileEl.hidden = wrapEl.hidden = !has;
3310
+ if (!has) return;
3311
+ if (stats) renderStatsTile(stats);
3312
+ renderTracesList(items);
3313
+ hintEl.textContent = count > items.length
3314
+ ? `${items.length} of ${count}` : `${count} session${count === 1 ? '' : 's'}`;
3315
+ } catch { /* traces are best-effort; never disrupt the dashboard */ }
3316
+ }
3317
+
3318
  let wikiCharts = {};
3319
  // epoch ms -> "MM-DD HH:MM" (UTC) for the time axis ticks
3320
  function fmtTick(v) {