Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Ollama Cloud Usage Stats
- // @namespace https://ollama.com/
- // @version 2.1.0
- // @description Extends the Ollama Cloud Usage dashboard: merges the weekly % into each model's request count and adds a per-1k-requests cost column (weekly table edited in place; same kind of table injected below the session bar).
- // @author you
- // @match https://ollama.com/settings
- // @match https://ollama.com/settings/*
- // @match https://ollama.com/settings?*
- // @run-at document-idle
- // @grant none
- // ==/UserScript==
- (function () {
- 'use strict';
- console.log('[ollama-stats] userscript loaded');
- // --- Shared column styles (header + value share widths/alignment) ---------
- const COL_USAGE =
- 'flex:none;min-width:6.5rem;text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;';
- const COL_PER1K =
- 'flex:none;min-width:4rem;text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;padding-left:.75rem;';
- // --- Track helpers ---------------------------------------------------------
- function trackBy(kind) {
- return Array.from(document.querySelectorAll('[data-usage-track]')).find(
- (t) => (t.getAttribute('aria-label') || '').toLowerCase().includes(kind)
- );
- }
- function segmentsOf(track) {
- if (!track) return null;
- const segs = Array.from(track.querySelectorAll('[data-usage-segment]'))
- .map((seg) => ({
- model: seg.dataset.model,
- requests: parseInt(seg.dataset.requests, 10) || 0,
- width: parseFloat((seg.style.width || '0').replace('%', '')) || 0,
- color: seg.style.background || '',
- }))
- .filter((s) => s.model);
- return segs.length ? segs : null;
- }
- // --- Derive the overall "used %" from the meter label --------------------
- function usedOf(track) {
- if (!track) return null;
- const m = (track.getAttribute('aria-label') || '').match(
- /([\d.]+)%\s*used/i
- );
- if (m) return parseFloat(m[1]);
- const inner = track.querySelector('[class*="bg-neutral-950"]');
- if (inner && inner.style.width) return parseFloat(inner.style.width) || null;
- return null;
- }
- // --- Compute stats ---------------------------------------------------------
- function computeStats(segs, used) {
- const usedPct = used != null ? used : 0;
- const rows = segs.map((s) => {
- const contrib = (s.width / 100) * usedPct;
- return {
- model: s.model,
- requests: s.requests,
- color: s.color,
- contrib,
- // Per-1000-request contribution (extrapolated): how much would 1,000
- // of these requests consume of the meter's limit?
- per1k: s.requests > 0 ? (contrib / s.requests) * 1000 : 0,
- };
- });
- rows.sort((a, b) => b.per1k - a.per1k);
- return { rows, used: usedPct };
- }
- // --- Shared table header ---------------------------------------------------
- function headerHTML(title) {
- return (
- '<span aria-hidden="true" style="flex:0 0 auto;width:.5rem;height:.5rem;"></span>' +
- '<span class="min-w-0 flex-1" style="color:#a3a3a3;">' + title + '</span>' +
- '<span class="flex-none" style="' + COL_USAGE + 'color:#a3a3a3;">Usage</span>' +
- '<span class="flex-none" style="' + COL_PER1K + 'color:#a3a3a3;">per 1k</span>'
- );
- }
- // --- Weekly table: edit the original list in place ------------------------
- function renderWeekly(stats) {
- const list = document.querySelector('#weekly-usage-models');
- if (!list) return;
- const byModel = new Map(stats.rows.map((r) => [r.model, r]));
- const rows = Array.from(list.querySelectorAll(':scope > div.flex'))
- .map((row) => ({
- row,
- r: byModel.get(row.querySelector('span[title]')?.getAttribute('title')),
- }))
- .filter((x) => x.r);
- if (!rows.length) return;
- rows.forEach(({ row, r }) => {
- // Merge the weekly % into the existing "N requests" text (true black).
- const reqSpan = row.querySelector('span.tabular-nums');
- if (reqSpan && !reqSpan.dataset.ossMerged) {
- reqSpan.dataset.ossMerged = '1';
- reqSpan.style.cssText = COL_USAGE + 'color:#a3a3a3;';
- reqSpan.innerHTML =
- reqSpan.textContent.trim() +
- ' (<span style="color:#111;">' + Math.round(r.contrib) + '%</span>)';
- }
- // Append the per-1k-requests column.
- if (!row.querySelector('[data-oss="pr"]')) {
- const pr = document.createElement('span');
- pr.dataset.oss = 'pr';
- pr.style.cssText = COL_PER1K + 'color:#404040;';
- pr.textContent = r.per1k.toFixed(1) + '%';
- row.appendChild(pr);
- }
- });
- // Header: reuse the original "Models used this week" label div.
- const label = Array.from(list.children).find(
- (el) => el.tagName === 'DIV' && !el.querySelector('span[title]')
- );
- if (label) {
- label.className = 'flex min-w-0 items-center gap-2 text-xs';
- label.innerHTML = headerHTML('Models used this week');
- } else if (!document.getElementById('oss-header')) {
- const header = document.createElement('div');
- header.id = 'oss-header';
- header.className = 'flex min-w-0 items-center gap-2 text-xs';
- header.innerHTML = headerHTML('Models used this week');
- list.insertBefore(header, list.firstElementChild);
- }
- // Reorder rows by per-1k cost (highest first).
- rows.sort((a, b) => b.r.per1k - a.r.per1k);
- rows.forEach(({ row }) => list.appendChild(row));
- console.log('[ollama-stats] weekly list updated');
- }
- // --- Session table: injected right below the session bar ------------------
- function renderSession(stats) {
- const track = trackBy('session');
- if (!track) return;
- const meter = track.closest('[data-usage-meter]') || track.parentElement;
- if (!meter || !meter.parentElement) return;
- let list = document.getElementById('session-usage-models');
- if (!list) {
- list = document.createElement('div');
- list.id = 'session-usage-models';
- list.className = 'mt-3 space-y-1.5';
- meter.insertAdjacentElement('afterend', list);
- }
- list.innerHTML = '';
- const header = document.createElement('div');
- header.className = 'flex min-w-0 items-center gap-2 text-xs';
- header.innerHTML = headerHTML('Models used this session');
- list.appendChild(header);
- stats.rows.forEach((r) => {
- const row = document.createElement('div');
- row.className = 'flex min-w-0 items-center gap-2 text-xs';
- const dot = document.createElement('span');
- dot.className = 'h-2 w-2 flex-none rounded-sm';
- dot.setAttribute('aria-hidden', 'true');
- dot.style.background = r.color || '#d4d4d4';
- const name = document.createElement('span');
- name.className = 'min-w-0 flex-1 truncate text-neutral-700';
- name.title = r.model;
- name.textContent = r.model;
- const req = document.createElement('span');
- req.style.cssText = COL_USAGE + 'color:#a3a3a3;';
- req.innerHTML =
- r.requests + (r.requests === 1 ? ' request' : ' requests') +
- ' (<span style="color:#111;">' + Math.round(r.contrib) + '%</span>)';
- const pr = document.createElement('span');
- pr.style.cssText = COL_PER1K + 'color:#404040;';
- pr.textContent = r.per1k.toFixed(1) + '%';
- row.append(dot, name, req, pr);
- list.appendChild(row);
- });
- console.log('[ollama-stats] session table rendered');
- }
- // --- Orchestration ---------------------------------------------------------
- function tryRun() {
- const wTrack = trackBy('weekly');
- if (!wTrack) return false;
- const wSegs = segmentsOf(wTrack);
- if (!wSegs) return false;
- renderWeekly(computeStats(wSegs, usedOf(wTrack)));
- const sTrack = trackBy('session');
- const sSegs = sTrack && segmentsOf(sTrack);
- if (sSegs) renderSession(computeStats(sSegs, usedOf(sTrack)));
- return true;
- }
- if (tryRun()) return;
- // Content loads asynchronously — observe for the bars to appear.
- const observer = new MutationObserver(() => {
- if (tryRun()) observer.disconnect();
- });
- observer.observe(document.documentElement, { childList: true, subtree: true });
- // Safety fallback: also poll for a few seconds.
- let attempts = 0;
- const poll = setInterval(() => {
- if (tryRun() || ++attempts > 30) {
- clearInterval(poll);
- if (!document.querySelector('#weekly-usage-models')) {
- console.warn(
- '[ollama-stats] never found segments — check that you are on ollama.com/settings'
- );
- }
- }
- }, 500);
- // Re-render if htmx swaps the page in-place.
- document.addEventListener('htmx:afterSwap', () => {
- document.getElementById('session-usage-models')?.remove();
- tryRun();
- });
- })();
Advertisement
Add Comment
Please, Sign In to add comment