fix(huggingface): list repos nested more than one directory deep (#12355)

The HuggingFace tree API returns each entry's path relative to the repo
root ("assets/plots", not "plots"). The recursive listing prefixed the
parent directory again, so it requested "assets/assets/plots", got a
404, and failed the whole listing. The importer then treated the URI as
a non-HF repo and no importer matched.

This broke the import of GGUF repos that keep per-quant subfolders next
to a nested assets tree, such as
ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF.

The test mock now returns root-relative directory paths like the real
API and routes on the exact tree path, so a doubled path 404s.

Assisted-by: Claude:claude-opus-5-5 [Claude Code]

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
mudler-agentandEttore Di Giacinto authored and GitHub committed 2026-09-29 10:37:31 +02:00
1 parent 2fe459ca5f
commit b700da3eb3
2 files changed
+19 -17

No files matched your search

+5 -11
View File
@@ -304,14 +304,12 @@ func (c *Client) listFilesInPath(repoID, path string) ([]FileInfo, error) {
switch item.Type {
// If it's a directory/folder, recursively list its contents
case "directory", "folder":
// Build the subfolder path
// The tree API returns every entry's path relative to the repo
// root ("assets/plots", not "plots"), so it is already the path
// to recurse into. Prefixing the parent again requested
// "assets/assets/plots", which 404s and failed the whole listing
// for any repo nested more than one directory deep.
subPath := item.Path
if path != "" {
subPath = fmt.Sprintf("%s/%s", path, item.Path)
}
// Recursively get files from subfolder
// The recursive call will already prepend the subPath to each file's path
subFiles, err := c.listFilesInPath(repoID, subPath)
if err != nil {
return nil, fmt.Errorf("failed to list files in subfolder %s: %w", subPath, err)
@@ -319,10 +317,6 @@ func (c *Client) listFilesInPath(repoID, path string) ([]FileInfo, error) {
allFiles = append(allFiles, subFiles...)
case "file":
// It's a file, prepend the current path to make it relative to root
// if path != "" {
// item.Path = fmt.Sprintf("%s/%s", path, item.Path)
// }
allFiles = append(allFiles, item)
}
}
+14 -6
View File
@@ -459,7 +459,7 @@ var _ = Describe("HuggingFace API Client", func() {
},
{
"type": "directory",
"path": "nested",
"path": "subfolder/nested",
"size": 0,
"oid": "nesteddir123"
}
@@ -483,15 +483,23 @@ var _ = Describe("HuggingFace API Client", func() {
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
urlPath := r.URL.Path
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if strings.Contains(urlPath, "/tree/main/subfolder/nested") {
// Route on the exact tree path: the real API 404s on a wrong
// path, and nested entries carry root-relative paths
// ("subfolder/nested"), so a prefix match would hide a
// doubled "subfolder/subfolder/nested" request.
_, treePath, _ := strings.Cut(urlPath, "/tree/main")
switch treePath {
case "/subfolder/nested":
w.WriteHeader(http.StatusOK)
w.Write([]byte(mockNestedResponse))
} else if strings.Contains(urlPath, "/tree/main/subfolder") {
case "/subfolder":
w.WriteHeader(http.StatusOK)
w.Write([]byte(mockSubfolderResponse))
} else if strings.Contains(urlPath, "/tree/main") {
case "":
w.WriteHeader(http.StatusOK)
w.Write([]byte(mockRootResponse))
} else {
default:
w.WriteHeader(http.StatusNotFound)
}
}))