path_ref.rs 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. //! Resolve `{{<name>}}` model-path references to container-side HF cache paths.
  2. use crate::config::{Config, Node};
  3. use crate::docker::{command, shq};
  4. use crate::ssh::run_ssh;
  5. use anyhow::Result;
  6. use std::path::Path;
  7. /// Resolve a model path reference's full HF cache path on a specific node by
  8. /// reading its `refs/main`: `<hf_cache>/hub/<model_cache_dir_name(repo)>/snapshots/<refs/main>[/<subfolder>]`.
  9. /// `refs/main` always points at the latest commit, so the path survives
  10. /// `huggingface-cli download` updates (no fragile symlink). If the repo or
  11. /// subfolder is absent, returns an error pointing at `huggingface-cli download`
  12. /// (the launcher never downloads).
  13. ///
  14. /// The resolved path is substituted into the recipe args/env via `{{<name>}}`
  15. /// — no bind-mount is created (the HF cache is already mounted at
  16. /// `/root/.cache/huggingface`, so the path's symlinks resolve correctly).
  17. pub(crate) async fn resolve_path_ref_on_node(
  18. config: &Config,
  19. node: &Node,
  20. spec: &command::PathRef,
  21. hf_cache: &Path,
  22. ) -> Result<String> {
  23. let model_dir = crate::config::model_cache_dir_name(&spec.repo);
  24. let sub = spec.subfolder.as_deref().unwrap_or("");
  25. let hub_dir = format!("{}/hub/{}", hf_cache.display(), model_dir);
  26. let refs_path = format!("{}/refs/main", hub_dir);
  27. // `cat refs/main` prints the current commit hash; trim trailing newline.
  28. // All literal path components are shell-quoted (a repo/subfolder with a
  29. // quote/space would otherwise break the command); `$ref` is our own
  30. // captured var, so it expands safely inside the printf double-quotes.
  31. let cmd = format!(
  32. "ref=\"$(cat {refs_q} 2>/dev/null)\" && test -n \"$ref\" && \
  33. dir=\"$(printf '%s/snapshots/%s/%s' {hub_q} \"$ref\" {sub_q})\" && \
  34. test -d \"$dir\" && printf '%s' \"$dir\"",
  35. refs_q = shq(&refs_path),
  36. hub_q = shq(&hub_dir),
  37. sub_q = shq(sub),
  38. );
  39. let resolved_path = run_ssh(config, node, cmd, false).await?;
  40. if resolved_path.trim().is_empty() {
  41. let hint = format!(
  42. "Model subfolder '{}' not found in HF cache on '{}': \
  43. repo '{}' is not downloaded there or 'refs/main' is missing. \
  44. Run `huggingface-cli download {}` on that node.",
  45. sub, node.name, spec.repo, spec.repo
  46. );
  47. anyhow::bail!(hint);
  48. }
  49. Ok(resolved_path.replace(&hf_cache.display().to_string(), "/root/.cache/huggingface"))
  50. }