mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-08-24 08:03:45 +00:00
fix(ruvllm-cli): follow HF 307 redirect on aux-file download (#590)
* docs(adr-259): mark RuvllmMutator implemented (code+tests+CLI in @metaharness/darwin); live-serve e2e blocked by ruvllm download redirect bug Co-Authored-By: claude-flow <ruv@ruv.net> * fix(ruvllm-cli): follow HF 307 redirect on aux-file download (curl -L fallback) `ruvllm download <model>` failed on aux files like tokenizer_config.json: 'Failed to download tokenizer_config.json'. The hf-hub API client doesn't follow HuggingFace's 307 redirect to the LFS/CDN host for these files (a plain `curl -L` on the same resolve URL returns 200). Add a redirect-following `curl -L --fail` fallback in download_with_progress(): try hf-hub first, fall back to curl from the HF resolve URL (https://huggingface.co/<id>/resolve/<rev>/<file>), honoring HF_TOKEN. curl is already the download mechanism in hub/download.rs, so this is dependency-free and consistent. Verified: tokenizer_config.json + config.json now download (2.9KB/2.5KB). Note: a SEPARATE pre-existing bug remains — GGUF weights are requested as an unexpanded glob '*<suffix>.gguf' (404), and the GGUF alias points at the safetensors repo; that needs HF file-listing + registry resolution and is out of scope for this redirect fix. Co-Authored-By: claude-flow <ruv@ruv.net> * style(ruvllm-cli): rustfmt Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: ruvnet <ruvnet@gmail.com>
This commit is contained in:
parent
5aa5353c87
commit
946275a611
2 changed files with 72 additions and 8 deletions
|
|
@ -90,7 +90,7 @@ pub async fn run(
|
|||
println!(" {} {}", style("Downloading:").yellow(), file_name);
|
||||
|
||||
// Download with progress
|
||||
let downloaded_path = download_with_progress(&repo, file_name).await?;
|
||||
let downloaded_path = download_with_progress(&repo, file_name, &model_id, revision).await?;
|
||||
|
||||
// Copy to cache directory
|
||||
tokio::fs::copy(&downloaded_path, &target_path)
|
||||
|
|
@ -123,10 +123,18 @@ pub async fn run(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Download a file with progress indication
|
||||
/// Download a file with progress indication.
|
||||
///
|
||||
/// Tries the `hf-hub` API first; on failure (e.g. the crate's HTTP client not
|
||||
/// following HuggingFace's 307 redirect to the LFS/CDN host — observed on aux files
|
||||
/// like `tokenizer_config.json` in 2.1.0), falls back to a redirect-following
|
||||
/// `curl -L --fail` from the HF resolve URL. curl is already the download mechanism
|
||||
/// in `hub/download.rs`, so this keeps the fix dependency-free and consistent.
|
||||
async fn download_with_progress(
|
||||
repo: &hf_hub::api::tokio::ApiRepo,
|
||||
file_name: &str,
|
||||
model_id: &str,
|
||||
revision: Option<&str>,
|
||||
) -> Result<PathBuf> {
|
||||
// Create progress bar
|
||||
let pb = ProgressBar::new(100);
|
||||
|
|
@ -137,17 +145,50 @@ async fn download_with_progress(
|
|||
.progress_chars("#>-"),
|
||||
);
|
||||
|
||||
// Download file
|
||||
let path = repo
|
||||
.get(file_name)
|
||||
.await
|
||||
.context(format!("Failed to download {}", file_name))?;
|
||||
// Download file (hf-hub API first, curl-redirect fallback on failure)
|
||||
let path = match repo.get(file_name).await {
|
||||
Ok(p) => p,
|
||||
Err(e) => download_via_curl(model_id, revision, file_name)
|
||||
.with_context(|| format!("Failed to download {} (hf-hub: {e})", file_name))?,
|
||||
};
|
||||
|
||||
pb.finish_and_clear();
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Fallback download via `curl -L --fail` (follows HF's 307 redirect to the CDN).
|
||||
/// Writes to a temp file and returns its path; the caller copies it into the cache.
|
||||
fn download_via_curl(model_id: &str, revision: Option<&str>, file_name: &str) -> Result<PathBuf> {
|
||||
let rev = revision.unwrap_or("main");
|
||||
let url = format!("https://huggingface.co/{model_id}/resolve/{rev}/{file_name}");
|
||||
let out = std::env::temp_dir().join(format!(
|
||||
"ruvllm-dl-{}-{}",
|
||||
std::process::id(),
|
||||
file_name.replace('/', "_")
|
||||
));
|
||||
let mut args = vec![
|
||||
"-L".to_string(), // follow redirects (the 307 hf-hub misses)
|
||||
"--fail".to_string(), // non-zero exit on HTTP error
|
||||
"-sS".to_string(), // quiet but show errors
|
||||
"-o".to_string(),
|
||||
out.to_string_lossy().to_string(),
|
||||
];
|
||||
if let Ok(token) = std::env::var("HF_TOKEN") {
|
||||
args.push("-H".to_string());
|
||||
args.push(format!("Authorization: Bearer {token}"));
|
||||
}
|
||||
args.push(url.clone());
|
||||
let status = std::process::Command::new("curl")
|
||||
.args(&args)
|
||||
.status()
|
||||
.with_context(|| format!("curl not available to fetch {url}"))?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("curl fallback failed ({status}) for {url}");
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Get list of files to download for a model and quantization
|
||||
fn get_files_to_download(model_id: &str, quant: QuantPreset) -> Vec<String> {
|
||||
let mut files = vec![
|
||||
|
|
|
|||
|
|
@ -1,6 +1,29 @@
|
|||
# ADR-259: ruvllm as Local Mutator Backend for Darwin Mode
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Implemented (code + unit tests + CLI; live-serve e2e blocked by a ruvllm download bug — see Implementation status)
|
||||
|
||||
## Implementation status (2026-06-18)
|
||||
|
||||
Implemented in `agent-harness-generator` (`@metaharness/darwin`):
|
||||
- `src/ruvllm-mutator.ts` — `RuvllmMutator implements CodeGenerator`, OpenAI-compatible
|
||||
`POST /v1/chat/completions`, safe no-op fallback if the server is unreachable. Exported from `index.ts`.
|
||||
- `src/cli.ts` — `--mutator deterministic|ruvllm` (+ `--ruvllm-url`/`--ruvllm-model`), wired to `config.generator`.
|
||||
- `__tests__/ruvllm-mutator.test.ts` — 4 tests vs a real `node:http` mock (success, fence-strip,
|
||||
unreachable→no-op, malformed→no-op). Full darwin suite **354/354** green.
|
||||
|
||||
**Honest gap:** the live-serve e2e (evolve against a real local model) is **blocked by a ruvllm
|
||||
2.1.0 download bug** — `ruvllm download phi` fails on `tokenizer_config.json` (the file is served
|
||||
via an HTTP 307 redirect that ruvllm does not follow; `curl -L` fetches it fine). So the HTTP
|
||||
*contract* is verified by unit tests, but an end-to-end run against a served model is pending a
|
||||
ruvllm fix (or manual model placement). Recommend fixing the redirect-follow in `ruvllm download`.
|
||||
|
||||
**Value note (ADR-087):** the mutator is not the quality lever — deterministic and frontier-LLM
|
||||
mutators both hit the 0.985 scorer ceiling. RuvllmMutator's benefit is *operational* (fully local,
|
||||
air-gapped, zero API cost), not higher scores.
|
||||
|
||||
---
|
||||
|
||||
**Original status:** Proposed
|
||||
**Date:** 2026-06-18
|
||||
**Components:**
|
||||
- `crates/ruvllm` — local LLM inference runtime (RDT/OpenMythos/GGUF)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue