Acceptance profiles¶
How far speculative decoding advances a request per verification step. The registry holds measured acceptance lengths with their provenance; a workload, a scenario or the command line may state their own; and a run resolves exactly one of them and records where it came from.
The quantity¶
The canonical value is the mean acceptance length (AL): the tokens a request gains per
verification step, counting the target's bonus token. For K speculative tokens it lies in
[1, K + 1]. It is what InferenceX's golden curves publish and what the engines take
(vLLM synthetic_acceptance_length, SGLang SGLANG_SIMULATE_ACC_LEN).
Two other quantities are derived from it, and each has one name here:
- The acceptance rate is the accepted fraction of drafts, (AL − 1) / K. It is vLLM's
logged "Avg Draft acceptance rate" and inference-sim's
--speculative-acceptance-rate. The two agree only when every step drafts all K: with a drafter that verifies fewer on some steps, such as DSpark underenable_adaptive_verification, vLLM's logged rate (accepted over drafted tokens,vllm/v1/spec_decode/metrics.py:107-110) divides by the drafts actually proposed and diverges from (AL − 1) / K. The per-token probability p of the speculative-decoding papers is a different quantity and is never called a rate in this schema. - Per-position rates p1..pK follow vLLM's
synthetic_acceptance_rates: pi is the unconditional probability that the first i drafts are all accepted. They are non-increasing, each lies in [0, 1], and they sum to AL − 1.
No source in scope publishes per-position rates, so they are optional everywhere. A
consumer that needs a distribution derives one with MinimumVarianceRates, which is
vLLM v0.31.0's _acceptance_length_to_rates (vllm/config/speculative.py:557-564):
This is how vLLM, and the InferenceX runs that force a published AL, realise one, so a simulator using it reproduces those runs. Natural acceptance spreads more than this schedule does, so per-request tails derived from it are optimistic.
Acceptance is not the kernel's input. K and the method stay in the deployment's
speculative block, because they change what a step costs; acceptance
changes only how far requests advance.
Profile set¶
A registry file under acceptance/, named by its file. This one carries real values from
InferenceX's golden SPEED-Bench curves:
kind: AcceptanceProfileSet
name: inferencex-golden-al
caveats:
- Coding only. Every curve is SPEED-Bench Qualitative coding; other categories have none.
- Measured on B300. Reuse on another part is recorded by the resolver.
- DeepSeek-V4.1-Flash with DSpark at K 6-8 was refused by the image, so it has no value.
profiles:
- model: deepseek-v4.1-flash
speculative_method: dspark
num_spec_tokens: 5
thinking: "off"
category: coding
acceptance_length: 4.07
method: measured
provenance: &dsv41
source: https://github.com/SemiAnalysisAI/InferenceX/blob/main/inferencex-e2e/infx/golden_al_distribution/dsv41flash_dspark.yaml
run: https://github.com/SemiAnalysisAI/InferenceX/actions/runs/34493175056
benchmark: SPEED-Bench Qualitative
prompts: 80
hardware: b300
engine_image: vllm/vllm-openai:deepseekv41-flash-0909
tp: 4
sampling: {temperature: 1.0, max_tokens: 4096}
settings:
draft_sample_method: probabilistic
rejection_sample_method: block
- model: deepseek-v4.1-flash
speculative_method: dspark
num_spec_tokens: 5
thinking: "on"
category: coding
acceptance_length: 3.51
method: measured
provenance: *dsv41
- model: glm-5.3
speculative_method: mtp
num_spec_tokens: 3
thinking: "on"
category: coding
acceptance_length: 2.99
method: copied
copied_from: glm-5.2-fp8/mtp/k3/thinking-on/coding
rationale: >
Provisional. GLM-5.3 shares GLM-5.2's base and layer topology, so GLM-5.2's curve is
the closest measured reference; replace it with a run on GLM-5.3.
provenance:
source: https://github.com/SemiAnalysisAI/InferenceX/blob/main/inferencex-e2e/infx/golden_al_distribution/glm5.3_mtp.yaml
run: https://github.com/SemiAnalysisAI/InferenceX/actions/runs/28058352479
hardware: b300
sampling: {temperature: 1.0, top_p: 0.95, max_tokens: 4096}
| Key | Type | Required | Meaning |
|---|---|---|---|
kind |
string | yes | Always AcceptanceProfileSet. |
name |
string | yes | The set's identity. By convention it equals the file name, which is what a scenario's acceptance.profiles uses. |
caveats |
list of strings | no | What the set does not cover and how far its values travel. No entry may be empty. |
profiles |
list | yes | At least one profile, below. |
Profiles¶
A profile is keyed by model, speculative method, K, thinking mode and workload category. Two profiles with one key are an error.
| Key | Type | Required | Meaning |
|---|---|---|---|
model |
string | yes | The catalog model name, not the source's spelling of it. |
speculative_method |
string | yes | The engine's method name, as a deployment's engine.speculative.method states it. |
num_spec_tokens |
integer | yes | K. At least 1. |
thinking |
string | yes | on or off. One of the thinking modes. |
category |
string | yes | The workload category, such as coding. Lower-case letters, digits, _ and -. |
acceptance_length |
number | yes | AL, in [1, K + 1]. |
per_position_rates |
list of numbers | no | K rates, each in [0, 1], non-increasing, summing to AL − 1 within 0.01. |
method |
string | yes | How the value was obtained. One of the methods except not_charged. |
provenance |
mapping | unless method is assumed |
Where it was measured, below. |
rationale |
string | unless method is measured |
The reasoning behind a value that was not measured. |
copied_from |
string | with method copied |
The profile it was copied from, as model/method/k<K>/thinking-<mode>/category. |
provenance¶
| Key | Type | Required | Meaning |
|---|---|---|---|
source |
string | yes | The document the value was read from. |
run |
string | no | The job that measured it, such as a GitHub Actions run. |
benchmark |
string | no | The prompt set, such as SPEED-Bench Qualitative. |
prompts |
integer | no | How many prompts it covered. Not negative. |
hardware |
string | with method measured |
The catalog chip it was measured on. A resolution on other hardware records this. |
engine_image |
string | with method measured |
The engine image or release that ran. |
tp |
integer | no | The tensor-parallel width it ran at. Not negative. |
sampling |
mapping | with method measured |
The decoding configuration, below. |
settings |
mapping | no | Other engine settings that bear on acceptance, by name, such as rejection_sample_method. |
provenance.sampling¶
| Key | Type | Required | Meaning |
|---|---|---|---|
temperature |
number | yes | Not negative. 0 is greedy decoding. |
top_p |
number | no | In (0, 1]. |
top_k |
integer | no | At least −1; −1 and 0 disable it. |
max_tokens |
integer | no | Output cap. Not negative. |
Inline values¶
A workload's acceptance, a scenario's acceptance.value, and the value a command line
supplies all take this form. A value names the K it was stated for; at a deployment that
speculates another K it is refused rather than rescaled.
| Key | Type | Required | Meaning |
|---|---|---|---|
num_spec_tokens |
integer | yes | The K the value was stated for. At least 1. |
acceptance_length |
number | one of three | AL, in [1, K + 1]. |
acceptance_rate |
number | one of three | (AL − 1) / K, in [0, 1]. Not with acceptance_length. |
per_position_rates |
list of numbers | one of three | K rates, as for a profile. Alone, they state AL as 1 + their sum; with either of the others, they must agree with it. |
source |
string | no | Where the value came from, recorded with the resolution. |
Scenario pin¶
The value of a scenario's acceptance key.
acceptance:
value:
num_spec_tokens: 3
acceptance_length: 2.8
source: synthetic_acceptance_length in the scored run's server log
To use another profile set for the run, and to accept coding's curve for traffic that has none of its own:
| Key | Type | Required | Meaning |
|---|---|---|---|
profiles |
string | no | An acceptance-profile set under acceptance/ in the registry, consulted in place of the default. Not with value. |
value |
mapping | no | An inline value, above. |
fallback_category |
string | no | The category to use when the workload's has no curve. The substitution is recorded. Not with value. |
A pin must set at least one key.
Resolution¶
acceptance.Resolve takes the run's model, method and K, the registry sets, the
workload's terms, the scenario's pin and the command line's value, and returns the
acceptance length, the per-position rates, and which layer supplied them. The highest
layer that states acceptance wins; layers are not merged.
| Layer | Supplies acceptance by |
|---|---|
registry |
the profile matching (model, method, K, the workload's thinking mode and category) in the registry sets, the default |
workload |
the workload's own acceptance value |
scenario |
the pin's value, or the matching profile in the set its profiles names |
cli |
the command line's value, such as --speculative-acceptance-rate at the deployment's K |
registry · workload · scenario · cli
Nothing falls back silently. A lookup is refused when
- the workload states no thinking mode, or no category and no fallback was requested;
- no profile matches and no
fallback_categorywas requested, or the fallback has no profile either; - two registry sets both match;
- the scenario names a set that was not supplied. The registry is not consulted in its place.
A value stated for a K other than the deployment's is refused at every layer. The resolution records the profile set and entry, the cited source, the category a fallback replaced, the hardware a profile was measured on when it differs from the run's, and whether the rates were derived.
Checks¶
A profile set is invalid if
kindis notAcceptanceProfileSet,nameis empty, there are no profiles, or a caveat is empty;- two profiles share a key;
- a profile has no
modelorspeculative_method,thinkingis notonoroff, orcategoryis malformed; num_spec_tokensis less than 1, oracceptance_lengthis not finite or lies outside [1, K + 1];per_position_rateshas other than K entries, an entry outside [0, 1], rises, or sums more than 0.01 from AL − 1;methodis not recognized or isnot_charged, or a field the method requires, per the table above, is missing;- a provenance has no
source, a negative count, an empty setting, or sampling with notemperature, a negative one, atop_poutside (0, 1] or atop_kbelow −1.
An inline value is invalid if num_spec_tokens is less than 1, it states no acceptance,
it states both acceptance_length and acceptance_rate, either lies outside its range,
or its rates are invalid or disagree with it. A pin is invalid if it sets nothing, sets
both profiles and value, sets fallback_category with value, or its value is
invalid.
With a deployment in the bundle, every inline value (the scenario's pin, its trace's, its replay's, merged with its catalog entry when the bundle carries one, a catalog replay's, and the workload shape's) must name the K of each pool that speculates. When no pool speculates, a stated value draws a warning.
Source: spec/acceptance.