Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 15 additions & 79 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions openinfer-engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,18 @@ pub struct LoadSnapshot {
pub num_running_reqs: u64,
/// Requests admitted but not yet running (KV pressure, prefetch wait).
pub num_waiting_reqs: u64,
/// Cumulative number of prompt tokens that queried the prefix cache.
///
/// Feeds the upstream `SchedulerStats.prefix_cache_stats.base.queries`
/// counter (`prefix_cache_queries_total`). Left 0 by schedulers that do not
/// yet track it; the bridge maps it through regardless so the metric
/// pipeline is wired end-to-end.
pub prefix_cache_queries: u64,
/// Cumulative number of prompt tokens served from the prefix cache
/// (i.e. prefix-cache hits). Maps to
/// `SchedulerStats.prefix_cache_stats.base.hits`
/// (`prefix_cache_hits_total`).
pub prefix_cache_hits: u64,
}

/// One full KV block that just became reusable from this engine's prefix cache.
Expand Down
1 change: 1 addition & 0 deletions openinfer-glm52/src/scheduler/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ pub(super) fn publish_load(
kv_total_blocks: kv_total_blocks as u64,
num_running_reqs: slots.iter().flatten().count() as u64,
num_waiting_reqs: pending.len() as u64,
..Default::default()
});
}
25 changes: 25 additions & 0 deletions openinfer-qwen3/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,12 +515,17 @@ fn publish_load<E: ModelExecutor>(
executor: &E,
num_running_reqs: u64,
num_waiting_reqs: u64,
prefix_cache_queries: u64,
prefix_cache_hits: u64,
) {
load_tx.send_replace(LoadSnapshot {
kv_used_blocks: kv_total.saturating_sub(executor.available_blocks() as u64),
kv_total_blocks: kv_total,
num_running_reqs,
num_waiting_reqs,
prefix_cache_queries,
prefix_cache_hits,
..Default::default()
});
}

Expand Down Expand Up @@ -552,6 +557,10 @@ fn scheduler_loop<E>(
// Decode-overlap async prefill: pending requests whose prefill is in-flight
// on the prefill overlap stream. `None` when no async prefill is running.
let mut inflight_prefill_pending: Option<Vec<PendingRequest>> = None;
// Prefix-cache counters surfaced to the vLLM frontend metrics. Accumulated
// across the scheduler's lifetime from each step's first-chunk queries/hits.
let mut prefix_cache_queries: u64 = 0;
let mut prefix_cache_hits: u64 = 0;

info!("Scheduler ready");

Expand All @@ -564,6 +573,8 @@ fn scheduler_loop<E>(
+ prefilling.len()
+ inflight_prefill_pending.as_ref().map_or(0, Vec::len)) as u64,
(deferred.len() + loading.len()) as u64,
prefix_cache_queries,
prefix_cache_hits,
);
// Flush the prior step's cache changes to a router (no-op unless the
// event feed is on). Top-of-loop, like `publish_load`: one pass per
Expand All @@ -590,6 +601,8 @@ fn scheduler_loop<E>(
scheduled_at_unix_s,
};
let effects = resolve_step(&executor, &active, artifacts);
prefix_cache_queries += effects.prefix_queries;
prefix_cache_hits += effects.prefix_hits;
Comment on lines +604 to +605
apply_effects(
&mut executor,
&mut active,
Expand Down Expand Up @@ -724,6 +737,8 @@ fn scheduler_loop<E>(

// Only apply decode effects from the unified result.
let effects = resolve_step(&executor, &active, artifacts);
prefix_cache_queries += effects.prefix_queries;
prefix_cache_hits += effects.prefix_hits;
apply_effects(
&mut executor,
&mut active,
Expand Down Expand Up @@ -754,6 +769,8 @@ fn scheduler_loop<E>(
}
};
let effects = resolve_step(&executor, &active, artifacts);
prefix_cache_queries += effects.prefix_queries;
prefix_cache_hits += effects.prefix_hits;
apply_effects(
&mut executor,
&mut active,
Expand Down Expand Up @@ -783,6 +800,10 @@ fn scheduler_loop_with_lora_control<E>(
let mut pending_control: VecDeque<EngineControlRequest> = VecDeque::new();
let mut post_control_deferred: Vec<PendingRequest> = Vec::new();
let mut tracker = phase_trace::PhaseTracker::default();
// Prefix-cache counters surfaced to the vLLM frontend metrics. Accumulated
// across the scheduler's lifetime from each step's first-chunk queries/hits.
let mut prefix_cache_queries: u64 = 0;
let mut prefix_cache_hits: u64 = 0;

info!("Scheduler ready with LoRA control");

Expand All @@ -793,6 +814,8 @@ fn scheduler_loop_with_lora_control<E>(
&executor,
(active.len() + prefilling.len()) as u64,
(deferred.len() + loading.len() + post_control_deferred.len()) as u64,
prefix_cache_queries,
prefix_cache_hits,
);

// 1. Drain incoming commands. Generation submitted after a pending
Expand Down Expand Up @@ -925,6 +948,8 @@ fn scheduler_loop_with_lora_control<E>(
}
};
let effects = resolve_step(&executor, &active, artifacts);
prefix_cache_queries += effects.prefix_queries;
prefix_cache_hits += effects.prefix_hits;
apply_effects(
&mut executor,
&mut active,
Expand Down
11 changes: 11 additions & 0 deletions openinfer-qwen3/src/scheduler/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ pub(super) struct StepEffects {
pub(super) prompt_echoes: Vec<PromptEchoEffect>,
pub(super) pending: Vec<PendingEffect>,
pub(super) decode: Vec<DecodeEffect>,
/// Prefix-cache queries counted this step: one per request whose first
/// prefill chunk ran (a `queries` increment in vLLM terms). Carried into
/// `LoadSnapshot.prefix_cache_queries` for the vLLM frontend metrics.
Comment on lines +96 to +98
pub(super) prefix_queries: u64,
/// Prefix-cache hit tokens counted this step: the sum of `cached_tokens`
/// across first-chunk requests (a `hits` increment in vLLM terms, token
/// granularity rather than block). Carried into
/// `LoadSnapshot.prefix_cache_hits` for the vLLM frontend metrics.
pub(super) prefix_hits: u64,
}

impl StepEffects {
Expand All @@ -102,6 +111,8 @@ impl StepEffects {
prompt_echoes: Vec::new(),
pending: Vec::new(),
decode: Vec::new(),
prefix_queries: 0,
prefix_hits: 0,
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions openinfer-qwen3/src/scheduler/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ pub(super) fn resolve_step(
prompt_echoes: Vec::new(),
pending: Vec::new(),
decode: resolve_decode_outputs(executor, active, &result.requests),
prefix_queries: 0,
prefix_hits: 0,
},
ExecutionArtifacts::SpeculativeDecode { verify } => StepEffects {
scheduled: Vec::new(),
prompt_echoes: Vec::new(),
pending: Vec::new(),
decode: resolve_speculative_outputs(executor, active, &verify.requests),
prefix_queries: 0,
prefix_hits: 0,
},
ExecutionArtifacts::Unified {
pending,
Expand Down Expand Up @@ -126,6 +130,10 @@ fn resolve_prefill_outputs(
prompt_tokens: prompt_len,
cached_tokens: result.cached_tokens,
});
// First chunk is the only place a request counts toward the
// prefix-cache query total; its cached token span is the hit total.
effects.prefix_queries += 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count prefix-cache queries by tokens

This increments the query counter once per request while prefix_cache_hits is already a token count from cached_tokens; vLLM's prefix-cache query metric is token-granularity. With any prompt longer than one token, especially a warm 1k-token prompt with hundreds of cached tokens, the exported counters can report far more hits than queries and make hit-rate calculations nonsensical. Add prompt_len for the first chunk instead of 1 so the denominator matches the hit units.

Useful? React with 👍 / 👎.

effects.prefix_hits += result.cached_tokens as u64;
Comment on lines +133 to +136
}

if !result.completed {
Expand Down
Loading