Browse Source

flare: long-running package/luanch ssh; ledger by recipe dir

Alexander Korolev 12 hours ago
parent
commit
6c3a20d976
3 changed files with 23 additions and 7 deletions
  1. 8 1
      CHANGELOG.md
  2. 4 4
      src/native/flare.rs
  3. 11 2
      src/native/mod.rs

+ 8 - 1
CHANGELOG.md

@@ -11,7 +11,14 @@ own version and its own `sm121/CHANGELOG.md`; do not mix them.
 - FLARE: `flare download` gets `--tensor-parallel` from the recipe's launch
   size; a TP4 package is no longer rejected as "TP size 1".
 - FLARE: local mirror copies the package directory recursively, including
-  `weights/` and `deployments/` companions.
+  `weights/` and `deployments/` companions; it clears the destination first
+  and runs over a long-lived SSH session (read-only package files and a 500 GB
+  copy must not be killed by the 5 s inactivity timeout).
+- FLARE: `flare download` and `flare orbit run` also use long-running SSH, so
+  a cached-package verification or a detached launch is not cut short.
+- FLARE ledger: `serve` records the recipe **directory** name, not the display
+  `name` field — the old `recipe.name` lookup failed with
+  `recipe dir not found for 'DeepSeek-V4.1-Flash-1017 (…) '`.
 - FLARE `gb10`: production recipe updated to the 2026-10-27 package
   (`c32`, 1 GiB row cache, resident scales, `--served-model-name`).
 - Tooling: `sm121` installed with `cargo install --path . --force`; version

+ 4 - 4
src/native/flare.rs

@@ -239,8 +239,8 @@ pub fn mirror_cmd(local_dir: &str, remote_pkg: &str) -> String {
     // is already the package directory itself.
     format!(
         "set -e; src=$(dirname {pkg}); dst={dir}; \
-         mkdir -p \"$dst\"; \
          if [ \"$src\" = \"$dst\" ]; then echo {pkg}; exit 0; fi; \
+         rm -rf \"$dst\"; mkdir -p \"$dst\"; \
          cp -a \"$src\"/. \"$dst\"/; \
          echo \"$dst/$(basename {pkg})\""
     )
@@ -299,7 +299,7 @@ pub async fn serve(
         info!("FLARE agents ok on {} node(s)", cluster.nodes.len());
     }
     let dl = download_cmd(config, recipe)?;
-    let package = crate::ssh::run_ssh(config, controller, dl, dry_run).await?;
+    let package = crate::ssh::run_ssh_long_running(config, controller, dl, dry_run).await?;
     let mut package = parse_package_path(&package).unwrap_or_default();
     // A cached download prints only "Resolved package: … / model.safetensors"
     // and no final path; resolve the default prepared model from Flare's
@@ -332,7 +332,7 @@ pub async fn serve(
     if let Some(dir) = local_dir_for(config, &cluster.name) {
         let dir = dir.replace('~', "$HOME");
         let cmd = mirror_cmd(&dir, &package);
-        let out = crate::ssh::run_ssh(config, controller, cmd, dry_run).await?;
+        let out = crate::ssh::run_ssh_long_running(config, controller, cmd, dry_run).await?;
         if !dry_run {
             let local = out.lines().last().unwrap_or_default().trim().to_string();
             if local.is_empty() {
@@ -343,7 +343,7 @@ pub async fn serve(
     }
     let orbit_name = orbit_node_name(config, &cluster.name, controller);
     let run = run_cmd(config, recipe, &cluster.name, &orbit, &orbit_name, &package)?;
-    crate::ssh::run_ssh(config, controller, run.clone(), dry_run).await?;
+    crate::ssh::run_ssh_long_running(config, controller, run.clone(), dry_run).await?;
     if dry_run {
         println!("  [dry-run] {}: {run}", controller.name);
     } else {

+ 11 - 2
src/native/mod.rs

@@ -139,7 +139,15 @@ pub async fn native_serve(
     let dry_run = opts.dry_run;
     let recipe_obj = crate::config::load_recipe(recipe_name, &config.recipe_sources).await?;
     if recipe_obj.engine == "flare" {
-        return flare_serve(config, &recipe_obj, cluster_name, node_name, opts).await;
+        return flare_serve(
+            config,
+            &recipe_obj,
+            recipe_name,
+            cluster_name,
+            node_name,
+            opts,
+        )
+        .await;
     }
     let dgpp = recipe_obj
         .dgpp
@@ -265,6 +273,7 @@ pub async fn native_serve(
 async fn flare_serve(
     config: &Config,
     recipe: &Recipe,
+    recipe_name: &str,
     cluster_name: &str,
     node_name: Option<&str>,
     opts: &ServeOpts,
@@ -314,7 +323,7 @@ async fn flare_serve(
     // is as real as a container, so it must be recorded (otherwise a second
     // launcher sees "no open run" and starts a second lane on the same nodes).
     if !opts.dry_run {
-        crate::runlock::mark_start(&recipe.name, &config.recipe_sources, Some(&cluster.name))?;
+        crate::runlock::mark_start(recipe_name, &config.recipe_sources, Some(&cluster.name))?;
     }
     Ok(cluster)
 }