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
3 changes: 3 additions & 0 deletions Cargo.lock

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

22 changes: 17 additions & 5 deletions docs/subsystems/frontend/simulated-inference-engine.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Simulated Inference Engine

> **TL;DR:** `pegainfer-sim` is a CPU-only `Scheduler` that serves through the vLLM/OpenAI frontend with configurable TTFT/TPOT. It launches as `LaunchedEngine::Stepped`. It is a frontend/bench harness, not a real-model performance path.
> **TL;DR:** `pegainfer-sim` is a CPU-only `Scheduler` that serves through the vLLM/OpenAI frontend with profile-priced engine steps or legacy-compatible timing flags. It launches as `LaunchedEngine::Stepped`. It is a frontend/bench harness, not a real-model performance path.
>
> **Last touched:** 2026-08
> **Last touched:** 2026-09

## Scope

Expand All @@ -16,9 +16,21 @@ Out of scope:

## Behavior

CLI knobs: model id, port, max model length, base TTFT, prefill throughput, TPOT, fallback token id.

Timing: TTFT is `base_ttft_ms + prompt_len / prefill_tokens_per_ms`; TPOT is a fixed delay between generated tokens. `SimScheduler::step` emits at most one token per request per step and parks up to 1ms while waiting, so a CPU-only sim does not spin a core the way a GPU scheduler can.
CLI knobs: model identity, optional local metadata path, port, max model length,
legacy base TTFT/prefill throughput/TPOT, fallback token id, profile path and
strict out-of-domain handling. `--profile <file>` loads a versioned engine profile;
its scheduler limits and model context are authoritative. `--model-path <path>`
selects the local tokenizer/config directory used by the frontend when the
profile's target model identity is not itself a local path. Legacy timing flags
cannot be combined with an explicit profile.

Timing: with a profile, `SimScheduler::step` prices one worker step from its
decode/prefill shape and commits progress only after that step duration. Without
an explicit profile, the CLI keeps the legacy per-request scheduler, preserving
`base_ttft_ms + prompt_len / prefill_tokens_per_ms` for prefill and fixed
`tpot_ms` for subsequent decode independently of batch width. The Rust API has
the same legacy behavior when callers do not attach a profile. The standalone
CLI initializes stderr logging so out-of-domain profile fallbacks remain visible.

Output token ids cycle through the prompt tokens, or replay a scripted sequence (tool-call tests). Empty prompts use the fallback id.

Expand Down
9 changes: 9 additions & 0 deletions pegainfer-frontend/src/engine/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ pub enum RejectReason {
max_tokens: usize,
limit: usize,
},
/// Whole-prefill scheduling cannot fit the request in one scheduler step.
PrefillStepBudget { prompt_tokens: usize, limit: usize },
/// Echo needs all-position logits in one forward pass, so the prompt must
/// fit the profiled prefill bound.
EchoPrefillTokens { prompt_tokens: usize, limit: usize },
Expand Down Expand Up @@ -201,6 +203,13 @@ impl fmt::Display for RejectReason {
requested {} (prompt={prompt_tokens} + max_tokens={max_tokens})",
prompt_tokens.saturating_add(*max_tokens)
),
Self::PrefillStepBudget {
prompt_tokens,
limit,
} => write!(
f,
"request prompt has {prompt_tokens} tokens but the whole-prefill step budget is {limit} tokens"
),
Self::EchoPrefillTokens {
prompt_tokens,
limit,
Expand Down
5 changes: 4 additions & 1 deletion pegainfer-sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ path = "src/main.rs"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true }
log = { workspace = true }
logforth = { workspace = true }
pegainfer-frontend = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["full"] }

[dev-dependencies]
reqwest = { workspace = true, features = ["json"] }
serde_json = { workspace = true }
tempfile = { workspace = true }
tokio-util = { workspace = true }

Expand Down
Loading
Loading