fix(model-store): 接受 HF tree API 返回的裸 LFS oid,修复本地模型下载失败 (#1072)
* test(model-store): 复现 HF tree API 返回裸 LFS oid 时模型下载被拒
huggingface.co 与 hf-mirror.com 的 /api/models/{repo}/tree 接口里 lfs.oid
是不带 sha256: 前缀的 64 位十六进制,现有解析只认前缀形式,导致本地模型
下载直接报 unsupported Hugging Face LFS oid。
* fix(model-store): 接受 HF tree API 返回的裸 LFS oid,修复本地模型下载失败
2.0 起 parse_lfs_sha256 只接受 sha256: 前缀,但 huggingface.co 与
hf-mirror.com 的 tree 接口返回的是裸 64 位十六进制,导致下载 Qwen3-ASR
等本地模型时直接报 unsupported Hugging Face LFS oid。改为前缀可选,
仍校验 64 位十六进制。
This commit is contained in:
1 parent
c8bd7aa295
commit
4d011df66a
1 file changed
+28
-3
@@ -2485,10 +2485,10 @@ fn parse_hf_tree_page_for_entry(
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
/// The Hugging Face tree API returns `lfs.oid` as a bare SHA-256 hex digest;
|
||||
/// the `sha256:`-prefixed form (as in Git LFS pointer files) is accepted too.
|
||||
fn parse_lfs_sha256(oid: &str) -> Result<String, BackendError> {
|
||||
let digest = oid
|
||||
.strip_prefix("sha256:")
|
||||
.ok_or_else(|| invalid("unsupported Hugging Face LFS oid"))?;
|
||||
let digest = oid.strip_prefix("sha256:").unwrap_or(oid);
|
||||
if digest.len() != 64 || !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) {
|
||||
return Err(invalid("invalid Hugging Face LFS sha256 oid"));
|
||||
}
|
||||
@@ -2940,6 +2940,31 @@ mod tests {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hf_tree_accepts_bare_lfs_oid_from_live_api() {
|
||||
// Shape returned by huggingface.co and hf-mirror.com for Qwen/Qwen3-ASR-0.6B
|
||||
// (2026-09-13): `lfs.oid` is the bare hex digest, without a `sha256:` prefix.
|
||||
let entries = vec![serde_json::json!({
|
||||
"type": "file",
|
||||
"oid": "3d4b2a1f0e9c8b7a6d5e4f3a2b1c0d9e8f7a6b5c",
|
||||
"size": 1876091704u64,
|
||||
"path": "model.safetensors",
|
||||
"lfs": {
|
||||
"oid": "79d6cbd4c98c7bbffe9db2edac07f56cd6637d0d5944b27f6c2b8353840323ea",
|
||||
"size": 1876091704u64,
|
||||
"pointerSize": 135
|
||||
}
|
||||
})];
|
||||
let files = parse_hf_tree_page("Qwen/Qwen3-ASR-0.6B", "qwen3-asr-0.6b", &entries).unwrap();
|
||||
assert_eq!(files.len(), 1);
|
||||
assert_eq!(files[0].path, "model.safetensors");
|
||||
assert_eq!(files[0].size_bytes, 1_876_091_704);
|
||||
assert_eq!(
|
||||
files[0].sha256.as_deref(),
|
||||
Some("79d6cbd4c98c7bbffe9db2edac07f56cd6637d0d5944b27f6c2b8353840323ea")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hf_link_pagination_accepts_same_origin_and_rejects_redirected_origin() {
|
||||
let current = "https://huggingface.co/api/models/org/model/tree/main?limit=1000";
|
||||
|
||||
Reference in new issue
Block a user