You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I run ft serve behind a local supervisor that polls /v1/stats to keep a per-model speed history (prefill and decode tokens/s per session). /v1/stats has cumulative token counts (prompt_tokens_total, completion_tokens_total) and 5-second sliding-window rates, but no cumulative time. So a poller cannot turn two polls into a speed:
the window rates decay to zero between polls, so sampling them misses most of the work and depends on the poll interval;
ttft_mean_ms / p95_ms come from the request ring, which records no TTFT for buffered (non-streaming) requests and has no decode duration at all.
llama.cpp solves this with cumulative prompt_seconds_total / tokens_predicted_seconds_total counters: diff two scrapes, divide tokens by seconds.
Related but different: #503 / #504 put per-request timings into each API response. That helps the client that sent the request, not a separate process watching the server.
Proposed solution
Add cumulative timing to the requests object of /v1/stats, measured in StatsTracker (which already sees every reply at the frontend chokepoint):
prefill_seconds_total: per request, admission to its first output reply;
decode_seconds_total: per request, time between its later output replies;
decode_tokens_total: tokens carried by those later replies (tokens that arrive on the first reply are not counted as decode work, so overlap cannot inflate the rate);
cached_prompt_tokens_total: summed prefix-cache hits, so a poller can exclude tokens that were never forwarded.
Summed per request like llama.cpp's counters; existing fields unchanged. I have a small implementation with tests and will open a PR referencing this issue.
Alternatives considered
Integrating the window rates on the client side: the result depends on the poll interval and undercounts short requests.
Before you start
Applies to
Engine
What problem does this solve
I run
ft servebehind a local supervisor that polls/v1/statsto keep a per-model speed history (prefill and decode tokens/s per session)./v1/statshas cumulative token counts (prompt_tokens_total,completion_tokens_total) and 5-second sliding-window rates, but no cumulative time. So a poller cannot turn two polls into a speed:ttft_mean_ms/p95_mscome from the request ring, which records no TTFT for buffered (non-streaming) requests and has no decode duration at all.llama.cpp solves this with cumulative
prompt_seconds_total/tokens_predicted_seconds_totalcounters: diff two scrapes, divide tokens by seconds.Related but different: #503 / #504 put per-request timings into each API response. That helps the client that sent the request, not a separate process watching the server.
Proposed solution
Add cumulative timing to the
requestsobject of/v1/stats, measured inStatsTracker(which already sees every reply at the frontend chokepoint):prefill_seconds_total: per request, admission to its first output reply;decode_seconds_total: per request, time between its later output replies;decode_tokens_total: tokens carried by those later replies (tokens that arrive on the first reply are not counted as decode work, so overlap cannot inflate the rate);cached_prompt_tokens_total: summed prefix-cache hits, so a poller can exclude tokens that were never forwarded.Summed per request like llama.cpp's counters; existing fields unchanged. I have a small implementation with tests and will open a PR referencing this issue.
Alternatives considered
/metricsendpoint with timing histograms (as in the withdrawn feat(server): export tracker metrics and scheduler queue telemetry #513): would also work, but adds a dependency and a second endpoint for what four counters on the existing endpoint cover.