Repository navigation
feat(llm): per-stream decode + effective prefill tok/s for TensorFold - #252
Closed
EagleClaw89 wants to merge 2 commits into
Closed
EagleClaw89 wants to merge 2 commits into
EagleClaw89 wants to merge 2 commits into
Conversation
… /metrics builds Decode: perStreamTps = 1 / mean seconds per output token over the last window, from tensorfold:request_time_per_output_token_seconds. The histogram moves only when a request finishes, so the value is held (not zeroed) between completions and falls back to aggregate / active streams (null when idle and nothing measured). Prefill: prompt_tokens_total and the prefill histograms advance ONLY in the poll where a request finishes (verified on a live server: a 35 s prefill moves nothing until it completes), so no mid-prefill rate exists. Derive the per-request effective prefill rate from delta prompt tokens / delta prefill seconds at completion and hold it; show 'prefilling' while a request is open with generation_tokens_running at 0 — the only live prefill signal this surface has. prompt_tokens_total includes prefix-cache tokens and /metrics has no cached counter, so the number is an effective rate, labelled as such. Running count: TensorFold exports requests_running and the num_requests_running alias with identical values; read the canonical name, fall back to the alias only when absent, never sum (test pins the single-stream case against double counting). Exposed as sparkdash_llm_per_stream_tokens_per_second and sparkdash_llm_per_stream_prefill_tokens_per_second on the Prometheus endpoint.
… cards and the LLM panel Decode tile gains 'N tok/s per stream'; Prefill tile gains 'N tok/s per request — effective (incl. cached tokens)' so prefix-cache hits are never presented as raw GPU speed. Both lines omit themselves when the engine reports nothing.
Owner
|
now on main as fa16c54, with the UI in d37dcc4. TensorFold completion-window counters feed per-stream decode and effective prefill fields, retaining noise guards and prefill expiry ( |
pavelkubicek
added a commit
to pavelkubicek/sparkDash
that referenced
this pull request
Oct 27, 2026
78 upstream commits: MQTT publishing (MiaAI-Lab#248), harness occupancy helpers (MiaAI-Lab#208), GPU clock cap from Settings (MiaAI-Lab#218), settings i18n foundation (MiaAI-Lab#226), configurable GPU temperature limits (MiaAI-Lab#273), Locals/Splash engines, TensorFold cache-counter fixes (MiaAI-Lab#252/MiaAI-Lab#260/MiaAI-Lab#272) and the zsh-safe remote probes (MiaAI-Lab#255/MiaAI-Lab#257). Conflict resolutions: - server/collectors/LlmProbe.js + its TensorFold test: took main's implementation, which supersedes this fork's /health `live` + /metrics work with the cache-counter precedence, running/waiting aliases, histogram validation and prefill holds. This drops the fork's parallel /metrics fetch (micro-optimisation only). - SystemCollector: main's zsh-safe find probes merged into the fork's build/parse refactor, keeping the fork-only RAPL package-power reads; _getRemoteNetwork now uses the injected executor (the merge had left the fork body calling sshExec, which made main's zsh-glob corpus test run real SSH). - OverviewPage: FleetKpis keeps the fork's placement but takes all sparks, so hidden cluster workers count in the KPI tiles (main's fix). - SparkPage: main's column layout (History in the hardware column, storage/network in the right column) over the fork's full-width History panel. - SettingsDialog: fork VulnHunter/REA opt-in toggles kept, metrics-history toggle switched to main's i18n keys. - index.js: fork vulnHunt/rea spark cleanup kept alongside MQTT removeUnit/applySettings. - CHANGELOG/README/package.json: both sides' entries kept; test globs unioned so the fork-only models/autopower/vulnhunt suites still run.
MiaAI-Lab
added a commit
that referenced
this pull request
Nov 6, 2026
The TensorFold tiles from #252 and #302 grew long captions ("Decode · aggregate", "40.0 tok/s · last completions", "Prefill · last completed", "Effective rate · includes cached tokens", "Not measured live") that made the Overview card taller than its numbers warrant. Keep the distinction they carry but say it in fewer words: "Decode · total" (tooltip: sum across running streams), "tok/s · completed", "Prefill · completed", "incl. cached tokens", "no live sample"; the worker-of hint is trimmed the same way. The full explanations stay in the hover tooltips. Both locales updated; two unused caption keys removed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Operators can see how hard a Spark is working (aggregate tok/s), but not how fast a conversation feels. Aggregate is throughput; per-stream is what a human experiences. This adds the missing half for TensorFold, measured from the engine's own counters.
What
perStreamTps— 1 / mean seconds per output token over the last window, fromtensorfold:request_time_per_output_token_seconds. The histogram moves only on request completion, so the value is held between completions (never flashes 0), falls back to aggregate ÷ active streams, and staysnullwhen nothing has been measured. Same design vocabulary as the existing Strata "active-slot mean", for TensorFold.perStreamPrefillTps— effective prefill rate per request. Verified live: during a 35.4 s / 98,854-token prefill nothing in/metricsmoves until the completion poll (prompt_tokens_total, the prefill histograms — all frozen; onlyrequests_runningchanges). The rate is therefore derived as Δprompt ÷ Δprefill-seconds at completion and held, with a live "prefilling" indicator fromrequests_running > 0 && generation_tokens_running == 0(verified:generation_tokens_runningstays 0 across the whole prefill, > 0 during decode).prompt_tokens_totalincludes prefix-cache hits and/metricspublishes no cached counter, so the UI labels it "effective (incl. cached tokens)" — a cache-served prompt correctly reads ~140k tok/s while cold prefill on the same box measured 2,803 tok/s (ground truth 98,854 ÷ 35.14 s).requests_runningandnum_requests_runningwith identical values (verified across whole requests). Read the canonical name; use the alias only when the canonical series is absent; never sum. A test pins a single stream reporting 1, not 2.Sanity gate + hold expiry on the prefill rate. Small prompts (the 1–200-token showcase/bench kind) and prefix-cache servings complete with sub-millisecond prefill times, so their Δprompt/Δprefill-seconds ratio is timer noise: observed live at 109k–313k tok/s against a verified cold-prefill ground truth of 2,803 tok/s (98,854 tokens ÷ 35.14 s, reproduced twice). Windows are therefore accepted only when Δprefill-seconds ≥ 50 ms, ≥ 50 prompt tokens, and an implied rate ≤ 15,000 tok/s (measured cold prefill on a DGX Spark is ~2.8–3.0k tok/s, so 15k is a 5x margin that keeps cache-credited agent windows — observed at 44k–313k — off the tile); rejected windows hold the last valid rate, and the hold expires after 20 s — the same staleness window the existing /health path already uses for its prefill estimate.
Exposed on the Prometheus endpoint as
sparkdash_llm_per_stream_tokens_per_secondandsparkdash_llm_per_stream_prefill_tokens_per_second. Both UI lines omit themselves when the engine reports nothing, so non-TensorFold cards are unchanged.Tests
prefillActiveheuristic) + 2 UI cases.node --test server/collectors/__tests__/LlmProbe.*.test.js(197 pass), vitest for touched pages,npm run typecheck,npm run build.requestsRunning: 1,prefillActive: truefor the full 35 s (tile says "prefilling"), thenprefillTps: 2803.6+perStreamPrefillTps: 2803.6held after completion; 2-stream bench →requestsRunning: 2,generationTps: 132.5,perStreamTps: 76.48.