diff --git a/pkg/distribution/distribution/client.go b/pkg/distribution/distribution/client.go index f150adef..82586068 100644 --- a/pkg/distribution/distribution/client.go +++ b/pkg/distribution/distribution/client.go @@ -123,6 +123,12 @@ func (c *Client) normalizeModelName(model string) string { return model } + // Normalize HuggingFace short URL (hf.co) to canonical form (huggingface.co) + // This ensures that hf.co/org/model and huggingface.co/org/model are treated as the same model + if rest, found := strings.CutPrefix(model, "hf.co/"); found { + model = "huggingface.co/" + rest + } + // If it looks like an ID or digest, try to resolve it to full ID if c.looksLikeID(model) || c.looksLikeDigest(model) { if fullID := c.resolveID(model); fullID != "" { diff --git a/pkg/distribution/distribution/normalize_test.go b/pkg/distribution/distribution/normalize_test.go index eff18597..26dd5567 100644 --- a/pkg/distribution/distribution/normalize_test.go +++ b/pkg/distribution/distribution/normalize_test.go @@ -123,6 +123,28 @@ func TestNormalizeModelName(t *testing.T) { input: "MyModel", expected: "ai/mymodel:latest", }, + + // HuggingFace URL normalization + { + name: "hf.co normalized to huggingface.co", + input: "hf.co/org/model", + expected: "huggingface.co/org/model:latest", + }, + { + name: "hf.co with tag normalized to huggingface.co", + input: "hf.co/org/model:Q4_K_M", + expected: "huggingface.co/org/model:Q4_K_M", + }, + { + name: "huggingface.co stays unchanged", + input: "huggingface.co/org/model", + expected: "huggingface.co/org/model:latest", + }, + { + name: "huggingface.co with tag stays unchanged", + input: "huggingface.co/org/model:Q4_K_M", + expected: "huggingface.co/org/model:Q4_K_M", + }, } for _, tt := range tests {