Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
216ef8c
feat(skippy): add Laguna staged runtime candidate
michaelneale Jul 27, 2026
2c56bf9
fix(models): preserve HF source revision in package jobs
michaelneale Jul 27, 2026
d94cc29
docs(skippy): record Laguna package certification
michaelneale Jul 27, 2026
8ea8b44
docs(skippy): record Laguna M5 parity
michaelneale Jul 28, 2026
226fb58
test(skippy): certify Laguna three-stage parity
michaelneale Jul 28, 2026
18b2128
docs(skippy): record Laguna distributed serving smoke
michaelneale Jul 28, 2026
6bd11c5
Merge remote-tracking branch 'origin/main' into feat/laguna-certifica…
michaelneale Jul 28, 2026
c408451
docs(skippy): record real Laguna mesh evidence
michaelneale Jul 28, 2026
7512430
fix(skippy): make stage memory truly layer-local
michaelneale Jul 28, 2026
a2e0d65
fix(skippy): clean partial prefix restores
michaelneale Jul 28, 2026
25727d9
fix(skippy): cancel timed out nonstream generation
michaelneale Jul 28, 2026
28afde2
skippy: recover hybrid verify windows without full KV copy
michaelneale Jul 28, 2026
c21ccdd
skippy: reclaim stage lanes after mid-request errors
michaelneale Jul 23, 2026
505cb97
refactor(skippy): isolate binary connection session tracking
michaelneale Jul 28, 2026
1fc4bb9
Merge common hybrid verify recovery into Laguna certification
michaelneale Jul 28, 2026
1d3ee4a
skippy: trim interleaved-SWA KV sessions
michaelneale Jul 28, 2026
181360e
Merge interleaved-SWA trim recovery into Laguna certification
michaelneale Jul 28, 2026
28e66d1
fix(packaging): preserve source revisions
michaelneale Jul 28, 2026
624fc7b
feat(skippy): honor package verification depth
michaelneale Jul 28, 2026
6ee8533
test(skippy): assert Laguna cache policy
michaelneale Jul 29, 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
35 changes: 31 additions & 4 deletions crates/mesh-llm-commands/src/model_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub async fn dispatch_model_package(args: ModelPrepareArgs<'_>) -> Result<()> {
let source_model_ref = model_ref::ModelRef::parse(source_ref)
.with_context(|| format!("invalid source model ref: {source_ref}"))?;
let source_repo = source_model_ref.repo.as_str();
let source_revision = source_model_ref.revision.as_deref();
let source_quant = match (source_model_ref.selector.as_deref(), quant) {
(Some(selector), Some(quant)) if selector != quant => {
bail!(
Expand All @@ -95,7 +96,7 @@ pub async fn dispatch_model_package(args: ModelPrepareArgs<'_>) -> Result<()> {
// If no quant specified, list available quants and exit.
// This path doesn't need HF_TOKEN — works for public repos.
if source_quant.is_none() {
return run_list_quants(&hf_client, source_repo, json).await;
return run_list_quants(&hf_client, source_repo, source_revision, json).await;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the selected revision in list output.

The quants are now discovered at the requested revision, but the follow-up example still emits repo:quant, and JSON omits the revision. Copying that example can package a different commit. Use the existing repo@revision:quant form and include sourceRevision in JSON.

Proposed fix
             serde_json::to_string_pretty(&json!({
                 "sourceRepo": source_repo,
+                "sourceRevision": source_revision,
                 "quants": quants,
             }))?

-    eprintln!("   mesh-llm models package {}:{}", source_repo, quants[0].name);
+    let source_ref = source_revision
+        .map(|revision| format!("{source_repo}@{revision}:{}", quants[0].name))
+        .unwrap_or_else(|| format!("{source_repo}:{}", quants[0].name));
+    eprintln!("   mesh-llm models package {source_ref}");

Also applies to: 256-262

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-commands/src/model_package.rs` at line 99, Update
run_list_quants so every listed quant preserves the requested revision: emit
examples using the repo@revision:quant format and include sourceRevision in JSON
output. Apply this consistently to both output paths, using the existing
source_revision value.

}

let submitting = confirm && !dry_run;
Expand All @@ -116,6 +117,7 @@ pub async fn dispatch_model_package(args: ModelPrepareArgs<'_>) -> Result<()> {
eprintln!("🔍 Resolving source...");
let params = PrepareParams {
source_repo: source_repo.to_string(),
source_revision: source_model_ref.revision.clone(),
quant: source_quant.map(|s| s.to_string()),
target: target.map(|s| s.to_string()),
model_id: model_id.map(|s| s.to_string()),
Expand Down Expand Up @@ -254,15 +256,17 @@ pub async fn dispatch_model_package(args: ModelPrepareArgs<'_>) -> Result<()> {
async fn run_list_quants(
client: &hf_hub::HFClient,
source_repo: &str,
source_revision: Option<&str>,
json_output: bool,
) -> Result<()> {
let quants = prepare::list_quants(client, source_repo).await?;
let quants = prepare::list_quants(client, source_repo, source_revision).await?;

if json_output {
println!(
"{}",
serde_json::to_string_pretty(&json!({
"sourceRepo": source_repo,
"sourceRevision": source_revision,
"quants": quants,
}))?
);
Expand All @@ -280,13 +284,20 @@ async fn run_list_quants(
eprintln!();
eprintln!("Specify one as a model ref, e.g.:");
eprintln!(
" mesh-llm models package {}:{}",
source_repo, quants[0].name
" mesh-llm models package {}",
source_quant_ref(source_repo, source_revision, &quants[0].name)
);

Ok(())
}

fn source_quant_ref(source_repo: &str, source_revision: Option<&str>, quant: &str) -> String {
source_revision.map_or_else(
|| format!("{source_repo}:{quant}"),
|revision| format!("{source_repo}@{revision}:{quant}"),
)
}

fn print_quant_table(quants: &[DiscoveredQuant]) {
// Find the longest name for alignment.
let max_name = quants.iter().map(|q| q.name.len()).max().unwrap_or(0);
Expand Down Expand Up @@ -639,4 +650,20 @@ mod tests {
fn parse_timeout_mixed() {
assert_eq!(parse_timeout("1h30m45s").unwrap(), 5445);
}

#[test]
fn source_quant_ref_preserves_revision() {
assert_eq!(
source_quant_ref("poolside/Laguna-S-2.1-GGUF", Some("abc123"), "Q4_K_M"),
"poolside/Laguna-S-2.1-GGUF@abc123:Q4_K_M"
);
}

#[test]
fn source_quant_ref_omits_absent_revision() {
assert_eq!(
source_quant_ref("poolside/Laguna-S-2.1-GGUF", None, "Q4_K_M"),
"poolside/Laguna-S-2.1-GGUF:Q4_K_M"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub(crate) fn stage0_config(
};
config.kv_cache = context
.family_policy
.stage_kv_cache_config_for_stage(&config);
.stage_kv_cache_config_for_package(&config, &context.package.package_dir);
config
}

Expand Down
103 changes: 99 additions & 4 deletions crates/mesh-llm-host-runtime/src/inference/skippy/family_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ impl FamilyPolicy {
pub(crate) fn stage_kv_cache_config_for_stage(
&self,
config: &StageConfig,
) -> Option<StageKvCacheConfig> {
self.stage_kv_cache_config_for_stage_with_meta(config, None)
}

pub(crate) fn stage_kv_cache_config_for_package(
&self,
config: &StageConfig,
package_dir: &Path,
) -> Option<StageKvCacheConfig> {
let metadata_path = package_dir.join("shared/metadata.gguf");
let metadata = scan_gguf_compact_meta(&metadata_path);
self.stage_kv_cache_config_for_stage_with_meta(config, metadata.as_ref())
}

fn stage_kv_cache_config_for_stage_with_meta(
&self,
config: &StageConfig,
package_meta: Option<&GgufCompactMeta>,
) -> Option<StageKvCacheConfig> {
match self.prefix_cache {
FamilyPrefixCachePolicy::Disabled { .. } => None,
Expand All @@ -58,7 +76,7 @@ impl FamilyPolicy {
min_tokens,
max_entries,
} => {
let max_bytes = derive_stage_cache_max_bytes(config)?;
let max_bytes = derive_stage_cache_max_bytes(config, package_meta)?;
// The family policy's `max_entries` is a generous
// upper bound on cache cardinality. The real ceiling
// is the unified KV cell pool size: each resident
Expand Down Expand Up @@ -316,18 +334,32 @@ fn wire_dtype_from_capability(dtype: WireDType) -> StageWireDType {
}
}

fn derive_stage_cache_max_bytes(config: &StageConfig) -> Option<u64> {
fn derive_stage_cache_max_bytes(
config: &StageConfig,
package_meta: Option<&GgufCompactMeta>,
) -> Option<u64> {
if let Some(max_bytes) =
package_meta.and_then(|meta| estimate_stage_cache_max_bytes(config, meta))
{
return Some(max_bytes);
}

[
config.materialized_path.as_deref(),
config.source_model_path.as_deref(),
config.model_path.as_deref(),
]
.into_iter()
.flatten()
.find_map(|path| scan_gguf_compact_meta(Path::new(path)))
.find_map(|path| scan_stage_cache_meta(Path::new(path)))
.and_then(|meta| estimate_stage_cache_max_bytes(config, &meta))
}

fn scan_stage_cache_meta(path: &Path) -> Option<GgufCompactMeta> {
scan_gguf_compact_meta(path)
.or_else(|| scan_gguf_compact_meta(&path.join("shared/metadata.gguf")))
}

fn estimate_stage_cache_max_bytes(config: &StageConfig, meta: &GgufCompactMeta) -> Option<u64> {
let stage_layers = config.layer_end.checked_sub(config.layer_start)?;
if stage_layers == 0 {
Expand Down Expand Up @@ -403,6 +435,8 @@ fn ggml_block_bytes(elements: u64, block_size: u64, type_size: u64) -> Option<u6

#[cfg(test)]
mod tests {
use std::fs;

use super::*;
use skippy_protocol::{FlashAttentionType, LoadMode};
use skippy_topology::{STAGE_RUNTIME_LLAMA_FAMILY_EXPECTATIONS, reviewed_capability_records};
Expand Down Expand Up @@ -468,6 +502,42 @@ mod tests {
}
}

fn push_gguf_string(bytes: &mut Vec<u8>, value: &str) {
bytes.extend_from_slice(&(value.len() as u64).to_le_bytes());
bytes.extend_from_slice(value.as_bytes());
}

fn push_gguf_u32(bytes: &mut Vec<u8>, key: &str, value: u32) {
push_gguf_string(bytes, key);
bytes.extend_from_slice(&4u32.to_le_bytes());
bytes.extend_from_slice(&value.to_le_bytes());
}

fn push_gguf_string_kv(bytes: &mut Vec<u8>, key: &str, value: &str) {
push_gguf_string(bytes, key);
bytes.extend_from_slice(&8u32.to_le_bytes());
push_gguf_string(bytes, value);
}

fn write_package_metadata(package_dir: &Path) {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"GGUF");
bytes.extend_from_slice(&2u32.to_le_bytes());
bytes.extend_from_slice(&0i64.to_le_bytes());
bytes.extend_from_slice(&8i64.to_le_bytes());
push_gguf_string_kv(&mut bytes, "general.architecture", "llama");
push_gguf_u32(&mut bytes, "llama.context_length", 8192);
push_gguf_u32(&mut bytes, "llama.embedding_length", 4096);
push_gguf_u32(&mut bytes, "llama.block_count", 32);
push_gguf_u32(&mut bytes, "llama.attention.head_count", 32);
push_gguf_u32(&mut bytes, "llama.attention.head_count_kv", 8);
push_gguf_u32(&mut bytes, "llama.attention.key_length", 128);
push_gguf_u32(&mut bytes, "llama.attention.value_length", 128);
let shared_dir = package_dir.join("shared");
fs::create_dir_all(&shared_dir).expect("create package shared directory");
fs::write(shared_dir.join("metadata.gguf"), bytes).expect("write package metadata");
}

#[test]
fn qwen_policy_comes_from_gguf_architecture() {
let policy = family_policy_for_gguf_meta(&meta("qwen3"), None);
Expand Down Expand Up @@ -608,7 +678,7 @@ mod tests {
| "minicpm3" | "plamo" | "plamo3" | "plm" | "refact" | "smallthinker"
| "smollm3" | "arcee" | "chatglm" | "codeshell" | "deci" | "xverse" | "apertus"
| "bitnet" | "command_r" | "starcoder" | "ernie4_5" | "ernie4_5_moe" | "qwen"
| "jais" | "jais2" | "nemotron" | "llama4" | "mistral4" | "seed_oss" => {
| "jais" | "jais2" | "nemotron" | "llama4" | "mistral4" | "seed_oss" | "laguna" => {
assert_eq!(
policy.prefix_cache,
FamilyPrefixCachePolicy::Auto {
Expand Down Expand Up @@ -761,4 +831,29 @@ mod tests {

assert!(estimate_stage_cache_max_bytes(&config, &kv_meta()).is_none());
}

#[test]
fn package_metadata_enables_cache_for_remote_package_paths() {
let package_dir = tempfile::tempdir().expect("package directory");
write_package_metadata(package_dir.path());
let mut config = stage_config();
config.materialized_path = None;
config.source_model_path = Some("/source/not-downloaded/model.gguf".to_string());
config.model_path = Some("hf://mesh-llm/laguna-layers".to_string());
let policy = FamilyPolicy {
activation_wire_dtype: StageWireDType::F16,
prefix_cache: FamilyPrefixCachePolicy::Auto {
payload: FamilyPrefixCachePayload::ResidentKv,
min_tokens: 256,
max_entries: 16,
},
};

let cache = policy
.stage_kv_cache_config_for_package(&config, package_dir.path())
.expect("package metadata should provide the cache byte budget");

assert_eq!(cache.payload, StageKvCachePayload::ResidentKv);
assert_eq!(cache.max_bytes, 3_211_264);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn native_mtp_generation() -> PackageGenerationInfo {
initial_window: 1,
min_window: 1,
max_window: 1,
pipeline_depth: None,
}),
proposer: Some("mtp".to_string()),
primary: None,
Expand Down Expand Up @@ -89,6 +90,7 @@ fn native_mtp_cache_generation() -> PackageGenerationInfo {
initial_window: 2,
min_window: 1,
max_window: 6,
pipeline_depth: None,
}),
proposer: None,
primary: Some("mtp".to_string()),
Expand Down Expand Up @@ -131,6 +133,7 @@ fn ngram_cache_generation() -> PackageGenerationInfo {
initial_window: 6,
min_window: 1,
max_window: 6,
pipeline_depth: None,
}),
proposer: Some("cache".to_string()),
primary: None,
Expand Down Expand Up @@ -173,6 +176,7 @@ fn ngram_suffix_generation() -> PackageGenerationInfo {
initial_window: 32,
min_window: 1,
max_window: 32,
pipeline_depth: Some(2),
}),
proposer: Some("suffix".to_string()),
primary: None,
Expand Down Expand Up @@ -239,6 +243,7 @@ fn speculative_strategy_auto_detects_direct_gguf_native_mtp_tensors() {

assert_eq!(resolved.speculative.strategy, "auto");
assert!(resolved.speculative.native_mtp_enabled);
assert_eq!(resolved.speculative.decode.verify_window.pipeline_depth, 1);
let load_options = resolved
.to_model_load_options(SkippyTelemetryOptions::off())
.expect("model load options should build");
Expand Down Expand Up @@ -451,12 +456,7 @@ strategy = "ngram-cache"

#[test]
fn package_suffix_strategy_resolves_as_a_standalone_proposer() {
let mesh_config = parse_config(
r#"
[defaults.speculative]
strategy = "ngram-suffix"
"#,
);
let mesh_config = parse_config("");
let model_file = temp_model_file();
let generation = ngram_suffix_generation();

Expand All @@ -476,10 +476,12 @@ strategy = "ngram-suffix"
resolved.speculative.decode.effective_strategy,
"ngram-suffix"
);
assert_eq!(resolved.speculative.decode.verify_window.pipeline_depth, 2);
let openai = resolved
.to_embedded_openai_args(4096, true)
.expect("package suffix strategy should build OpenAI args");
assert_eq!(openai.speculative_window, 48);
assert_eq!(openai.speculative.verify_window.pipeline_depth, 2);
assert_eq!(
openai.speculative.ngram.as_ref().map(|ngram| ngram.kind),
Some(skippy_server::NgramProposerKind::Suffix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ fn verify_window_config(policy: &PackageWindowPolicyInfo) -> VerifyWindowConfig
VerifyWindowConfig {
min_tokens: policy.min_window as usize,
max_tokens: policy.max_window as usize,
pipeline_depth: 1,
pipeline_depth: policy.pipeline_depth.unwrap_or(1) as usize,
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
collections::HashMap,
net::SocketAddr,
path::Path,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
Expand Down Expand Up @@ -738,7 +739,12 @@ fn stage_config(
downstream: load.downstream.as_ref().map(peer_config),
};
let family_policy = super::family_policy_for_stage_config(&config);
config.kv_cache = family_policy.stage_kv_cache_config_for_stage(&config);
config.kv_cache = package.map_or_else(
|| family_policy.stage_kv_cache_config_for_stage(&config),
|package| {
family_policy.stage_kv_cache_config_for_package(&config, Path::new(&package.local_ref))
},
);
Ok(config)
}

Expand Down
4 changes: 3 additions & 1 deletion crates/model-package/src/bin/queue-unsloth-layer-packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ async fn build_candidate(
return Ok(None);
}

let quants = match prepare::list_quants(client, &model.repo_id).await {
let quants = match prepare::list_quants(client, &model.repo_id, None).await {
Ok(quants) => quants,
Err(err) => {
eprintln!(
Expand Down Expand Up @@ -1096,12 +1096,14 @@ fn job_spec_with_token(
JobVolume {
volume_type: "bucket".into(),
source: "meshllm/layer-split-output".into(),
revision: None,
mount_path: "/bucket".into(),
read_only: None,
},
JobVolume {
volume_type: "model".into(),
source: candidate.model.repo_id.clone(),
revision: Some("main".into()),
mount_path: "/source".into(),
read_only: Some(true),
},
Expand Down
Loading
Loading