Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9df67ed
Enable adaptive verify window for ngram/draft speculation
michaelneale Jul 19, 2026
141ef39
Replace speculative rollback with positional MTP n-gram pipelining
i386 Jul 20, 2026
c06bca6
Pipeline speculative verify windows across latency
i386 Jul 20, 2026
55db388
Fix positional correction and adaptive pipeline depth
i386 Jul 20, 2026
0b3720c
Continuously refill the speculative horizon
i386 Jul 20, 2026
20e457a
Productionize pipelined MTP n-gram speculation
i386 Jul 20, 2026
87de12b
Fix speculative docs and UI formatting
i386 Jul 20, 2026
5b824cb
Remove stale speculative projections and fix CI
i386 Jul 20, 2026
f765bdd
feat(skippy): add suffix N-gram draft proposer (prompt-lookup decoding)
danielwinterw Jul 21, 2026
c697a70
docs(skippy): document suffix proposer + add benchmark runner
danielwinterw Jul 21, 2026
296c949
Handle fragmented direct-return fallback replies
i386 Jul 21, 2026
2af07bc
Replace speculative repair with fixed-depth positional pipeline
i386 Jul 21, 2026
68f4502
Expose split-stage compute overlap telemetry
i386 Jul 21, 2026
32e7ba4
Harden suffix N-gram proposer
i386 Jul 21, 2026
0d084df
Support standalone N-gram proposers
i386 Jul 21, 2026
7d175d6
Honor explicit Skippy prefix cache disable
i386 Jul 21, 2026
e83ff18
Report standalone speculative decode totals
i386 Jul 21, 2026
e5eb279
Document standalone proposer benchmark results
i386 Jul 21, 2026
7afcf95
Document MTP extension benchmark results
i386 Jul 21, 2026
bf13960
Address review: bound candidate scan, add docstrings
danielwinterw Jul 21, 2026
7f8ef6c
Lock split topology placement
i386 Jul 21, 2026
9160b31
Document locked split topology
i386 Jul 21, 2026
1d02631
Address locked topology review feedback
i386 Jul 21, 2026
bd940a2
Merge locked split topology for deterministic benchmarks
i386 Jul 21, 2026
479a4e1
Fix SPEED-Bench timing JSONL output
i386 Jul 22, 2026
c8227ef
Bound benchmark telemetry finalization
i386 Jul 22, 2026
6006848
Hash SPEED-Bench request and response pairs
i386 Jul 22, 2026
493d5bc
Forward standalone ngram limits for cache and suffix proposers
danielwinterw Jul 22, 2026
87e691a
mesh: stop re-applying formation-time RTT gate to operational stage s…
michaelneale Jul 22, 2026
34bd3b8
skippy: raise return-sink ready timeout 5s->20s for cold WAN bridge s…
michaelneale Jul 22, 2026
c05355a
runtime: relaunch withdrawn splits when peers return instead of endin…
michaelneale Jul 22, 2026
05d0e09
Merge upstream/agent/positional-mtp-ngram into feat/suffix-ngram-prop…
danielwinterw Jul 22, 2026
165db93
Merge upstream/main into feat/suffix-ngram-proposer
danielwinterw Jul 22, 2026
e461bb1
Address PR #1037 review: standalone N-gram edge cases
danielwinterw Jul 22, 2026
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
14 changes: 14 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ license = "MIT OR Apache-2.0"
version = "0.72.1"

[workspace.dependencies]
ahash = "0.8.12"
anyhow = "1"
blake3 = "1"
clap = { version = "4", features = ["derive"] }
Expand Down
45 changes: 44 additions & 1 deletion crates/mesh-llm-cli/src/parser/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,21 @@ impl MeshGuardrailCliMode {
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
pub enum SpeculativeNgramProposerCli {
Cache,
Suffix,
}

impl SpeculativeNgramProposerCli {
pub const fn as_str(self) -> &'static str {
match self {
Self::Cache => "cache",
Self::Suffix => "suffix",
}
}
}

#[derive(Parser, Debug)]
#[command(
name = "mesh-llm",
Expand Down Expand Up @@ -525,7 +540,7 @@ pub struct Cli {
pub auto_update: bool,

// ── Advanced options (hidden from default --help) ─────────────
/// Override the package speculative decoding strategy for this invocation.
/// Override speculative decoding (`mtp`, `ngram-cache`, `ngram-suffix`, or a package strategy id).
#[arg(long, hide = true)]
pub speculative_strategy: Option<String>,

Expand All @@ -541,6 +556,10 @@ pub struct Cli {
#[arg(long, hide = true)]
pub speculative_ngram_max_proposal_tokens: Option<u32>,

/// Standalone N-gram proposer kind (`cache` or `suffix`).
#[arg(long, hide = true, value_enum)]
pub speculative_ngram_proposer: Option<SpeculativeNgramProposerCli>,

/// Maximum N-gram extension length for a composite MTP strategy.
#[arg(long, hide = true)]
pub speculative_extension_max_tokens: Option<u32>,
Expand Down Expand Up @@ -1156,6 +1175,30 @@ mod tests {
assert_eq!(cli.speculative_verify_window_pipeline_depth, Some(3));
}

#[test]
fn serve_parses_standalone_suffix_strategy() {
let normalized = crate::parser::normalize_runtime_surface_args([
"mesh-llm",
"serve",
"--speculative-strategy",
"ngram-suffix",
"--speculative-ngram-proposer",
"suffix",
"--speculative-ngram-min",
"5",
"--speculative-ngram-max",
"32",
]);
let cli = Cli::try_parse_from(normalized.normalized).expect("clap parse");
assert_eq!(cli.speculative_strategy.as_deref(), Some("ngram-suffix"));
assert_eq!(
cli.speculative_ngram_proposer,
Some(SpeculativeNgramProposerCli::Suffix)
);
assert_eq!(cli.speculative_ngram_min, Some(5));
assert_eq!(cli.speculative_ngram_max, Some(32));
}

#[test]
fn auth_status_accepts_owner_key_locally() {
let cli = Cli::parse_from(["mesh-llm", "auth", "status", "--owner-key", "owner.json"]);
Expand Down
6 changes: 6 additions & 0 deletions crates/mesh-llm-config/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ pub struct SpeculativeConfig {
pub ngram_min: Option<u32>,
pub ngram_max: Option<u32>,
pub ngram_max_proposal_tokens: Option<u32>,
pub ngram_proposer: Option<String>,
pub extension_max_tokens: Option<u32>,
pub native_mtp_reject_cooldown_tokens: Option<u32>,
pub native_mtp_suppress_cooldown_drafts: Option<bool>,
Expand Down Expand Up @@ -608,6 +609,7 @@ impl SpeculativeConfig {
ngram_min: pick!(ngram_min),
ngram_max: pick!(ngram_max),
ngram_max_proposal_tokens: pick!(ngram_max_proposal_tokens),
ngram_proposer: pick!(ngram_proposer),
extension_max_tokens: pick!(extension_max_tokens),
native_mtp_reject_cooldown_tokens: pick!(native_mtp_reject_cooldown_tokens),
native_mtp_suppress_cooldown_drafts: pick!(native_mtp_suppress_cooldown_drafts),
Expand Down Expand Up @@ -674,6 +676,8 @@ struct SpeculativeConfigRaw {
#[serde(default)]
ngram_max_proposal_tokens: Option<u32>,
#[serde(default)]
ngram_proposer: Option<String>,
#[serde(default)]
extension_max_tokens: Option<u32>,
#[serde(default)]
native_mtp_reject_cooldown_tokens: Option<u32>,
Expand Down Expand Up @@ -724,6 +728,7 @@ impl<'de> Deserialize<'de> for SpeculativeConfig {
ngram_min: raw.ngram_min,
ngram_max: raw.ngram_max,
ngram_max_proposal_tokens: raw.ngram_max_proposal_tokens,
ngram_proposer: raw.ngram_proposer,
extension_max_tokens: raw.extension_max_tokens,
native_mtp_reject_cooldown_tokens: raw.native_mtp_reject_cooldown_tokens,
native_mtp_suppress_cooldown_drafts: raw.native_mtp_suppress_cooldown_drafts,
Expand Down Expand Up @@ -773,6 +778,7 @@ impl Serialize for SpeculativeConfig {
map.serialize_entry("ngram_min", &self.ngram_min)?;
map.serialize_entry("ngram_max", &self.ngram_max)?;
map.serialize_entry("ngram_max_proposal_tokens", &self.ngram_max_proposal_tokens)?;
map.serialize_entry("ngram_proposer", &self.ngram_proposer)?;
map.serialize_entry("extension_max_tokens", &self.extension_max_tokens)?;
map.serialize_entry(
"native_mtp_reject_cooldown_tokens",
Expand Down
4 changes: 4 additions & 0 deletions crates/mesh-llm-config/src/model/built_in_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,10 @@ fn speculative_settings(prefix: &str) -> Vec<ConfigSettingSchema> {
),
basic_setting(&format!("{prefix}.ngram_min"), ConfigValueSchema::Integer),
basic_setting(&format!("{prefix}.ngram_max"), ConfigValueSchema::Integer),
basic_setting(
&format!("{prefix}.ngram_proposer"),
string_enum(["cache", "suffix"]),
),
basic_setting(
&format!("{prefix}.ngram_max_proposal_tokens"),
ConfigValueSchema::Integer,
Expand Down
80 changes: 80 additions & 0 deletions crates/mesh-llm-config/src/model_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,27 @@ fn validate_speculative_proposer_controls(
config: &SpeculativeConfig,
base_path: &str,
) -> DiagnosticResult {
validate_optional_enum(
config.ngram_proposer.as_deref(),
&["cache", "suffix"],
&format!("{base_path}.ngram_proposer"),
)?;
let suffix_selected = config.ngram_proposer.as_deref() == Some("suffix")
|| config.strategy.as_deref() == Some("ngram-suffix");
if suffix_selected {
if config.ngram_min.is_some_and(|min| min < 3) {
return Err(validation_diagnostic(
&format!("{base_path}.ngram_min"),
format!("{base_path}.ngram_min must be at least 3 for the suffix proposer"),
));
}
if config.ngram_max.is_some_and(|max| max > 64) {
return Err(validation_diagnostic(
&format!("{base_path}.ngram_max"),
format!("{base_path}.ngram_max must not exceed 64 for the suffix proposer"),
));
}
}
validate_optional_u32_range(
config.ngram_max_proposal_tokens,
&format!("{base_path}.ngram_max_proposal_tokens"),
Expand Down Expand Up @@ -1221,4 +1242,63 @@ ctx_size = 8192
"expected no duplicate error for different derived profiles, got: {text}"
);
}

#[test]
fn suffix_proposer_rejects_matches_shorter_than_its_seed() {
let config: MeshConfig = toml::from_str(
r#"
version = 1

[defaults.speculative]
strategy = "mtp"
ngram_proposer = "suffix"
ngram_min = 2
ngram_max = 32
ngram_max_proposal_tokens = 16
"#,
)
.expect("config should parse before validation");

let text = legacy_validation_error_text(&validate_config_diagnostics(&config));
assert!(text.contains("ngram_min must be at least 3"), "{text}");
}

#[test]
fn suffix_proposer_rejects_windows_above_runtime_limit() {
let config: MeshConfig = toml::from_str(
r#"
version = 1

[defaults.speculative]
strategy = "mtp"
ngram_proposer = "suffix"
ngram_min = 5
ngram_max = 65
ngram_max_proposal_tokens = 16
"#,
)
.expect("config should parse before validation");

let text = legacy_validation_error_text(&validate_config_diagnostics(&config));
assert!(text.contains("ngram_max must not exceed 64"), "{text}");
}

#[test]
fn standalone_suffix_strategy_uses_suffix_validation_without_a_redundant_proposer_key() {
let config: MeshConfig = toml::from_str(
r#"
version = 1

[defaults.speculative]
strategy = "ngram-suffix"
ngram_min = 2
ngram_max = 65
ngram_max_proposal_tokens = 16
"#,
)
.expect("config should parse before validation");

let text = legacy_validation_error_text(&validate_config_diagnostics(&config));
assert!(text.contains("ngram_min must be at least 3"), "{text}");
}
}
Loading
Loading