The mirror would happily hold a four-shard model that nothing could serve. A catalog entry names one source, the agent fetched one URL, and so a version of several files had no way to be described — which is every safetensors model, and every GGUF too large to ship whole. A source ending in a slash names a version rather than a file. The agent fetches every file in it into one directory and hands the engine the directory: vLLM reads the shards and the config out of the folder, and llama.cpp is given the first part and finds the rest beside it. The trailing slash is the whole signal. A flag beside the source would be a second place for the two to disagree, and a URL already distinguishes a directory from a file everywhere else. The mirror answers a version with a manifest instead of a file listing. A plain file server gives HTML there, and something that has to know what a version consists of would be left parsing it. It is on the byte listener, not the management API: fetching weights is the one thing every node does, and making it depend on the administrative port would undo the split that keeps that port closeable. Built from the directory rather than a file written beside it. A manifest on disk is a second record of what is there, and the two disagree the first time somebody copies a file in by hand — which the mirror explicitly allows. The whole set or nothing. A directory with three of four shards loads as a broken model rather than failing to load, so a completion marker is written last and an interrupted fetch starts the missing files again rather than being found and served. Files already there at the right size are not fetched twice. Progress is reported under the version's address, not each file's: the shards waiting on it asked for the version, and progress under a file's own URL reaches nobody. A filename from the manifest is refused if it contains a separator. The mirror is a place an operator copies files into by hand, so what it reports is not automatically safe to join onto a path. An HTML listing where a manifest was expected says so. "invalid character '<'" sends somebody looking at the wrong thing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
168 lines
5.2 KiB
Go
168 lines
5.2 KiB
Go
package weights
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// mirrorOf serves a manifest at "/m/v/" and the files beneath it.
|
|
func mirrorOf(t *testing.T, files map[string]string) (*Fetcher, *httptest.Server, *int) {
|
|
t.Helper()
|
|
hits := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if strings.HasSuffix(r.URL.Path, "/") {
|
|
var b strings.Builder
|
|
b.WriteString(`{"model":"m","version":"v","files":[`)
|
|
first := true
|
|
total := 0
|
|
for name, body := range files {
|
|
if !first {
|
|
b.WriteString(",")
|
|
}
|
|
first = false
|
|
fmt.Fprintf(&b, `{"name":%q,"bytes":%d}`, name, len(body))
|
|
total += len(body)
|
|
}
|
|
fmt.Fprintf(&b, `],"bytes":%d}`, total)
|
|
_, _ = w.Write([]byte(b.String()))
|
|
return
|
|
}
|
|
body, ok := files[strings.TrimPrefix(r.URL.Path, "/m/v/")]
|
|
if !ok {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
hits++
|
|
_, _ = w.Write([]byte(body))
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
return &Fetcher{CacheDir: t.TempDir()}, srv, &hits
|
|
}
|
|
|
|
func TestASetIsFetchedIntoOneDirectory(t *testing.T) {
|
|
f, srv, _ := mirrorOf(t, map[string]string{
|
|
"model-00001-of-00002.safetensors": "aaaa",
|
|
"model-00002-of-00002.safetensors": "bb",
|
|
"config.json": "{}",
|
|
})
|
|
|
|
got, err := f.Ensure(t.Context(), srv.URL+"/m/v/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
st, err := os.Stat(got)
|
|
if err != nil || !st.IsDir() {
|
|
t.Fatalf("expected a directory, got %q: %v", got, err)
|
|
}
|
|
// A directory, because that is what a multi-file model is: vLLM reads the
|
|
// shards and the config out of the folder, and a catalog entry naming one
|
|
// file could express neither.
|
|
for _, name := range []string{"model-00001-of-00002.safetensors", "config.json"} {
|
|
if _, err := os.Stat(filepath.Join(got, name)); err != nil {
|
|
t.Errorf("%s did not arrive: %v", name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAFinishedSetIsNotFetchedAgain(t *testing.T) {
|
|
f, srv, hits := mirrorOf(t, map[string]string{"a.gguf": "aaaa", "b.gguf": "bb"})
|
|
|
|
if _, err := f.Ensure(t.Context(), srv.URL+"/m/v/"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
after := *hits
|
|
if _, err := f.Ensure(t.Context(), srv.URL+"/m/v/"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if *hits != after {
|
|
t.Errorf("the second call downloaded again: %d then %d", after, *hits)
|
|
}
|
|
}
|
|
|
|
func TestAHalfFetchedSetIsNotTreatedAsFinished(t *testing.T) {
|
|
f, srv, _ := mirrorOf(t, map[string]string{"a.gguf": "aaaa", "b.gguf": "bb"})
|
|
dir, err := f.cachePath(srv.URL + "/m/v")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// One file present, no completion marker — an interrupted download.
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "a.gguf"), []byte("aaaa"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := f.Ensure(t.Context(), srv.URL+"/m/v/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// A directory with three of four shards loads as a broken model rather
|
|
// than failing to load, so the marker is what says the set is whole.
|
|
if _, err := os.Stat(filepath.Join(got, "b.gguf")); err != nil {
|
|
t.Errorf("the missing file was not fetched: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestAFileAlreadyThereAtTheRightSizeIsNotRefetched(t *testing.T) {
|
|
f, srv, hits := mirrorOf(t, map[string]string{"a.gguf": "aaaa", "b.gguf": "bb"})
|
|
dir, _ := f.cachePath(srv.URL + "/m/v")
|
|
_ = os.MkdirAll(dir, 0o755)
|
|
_ = os.WriteFile(filepath.Join(dir, "a.gguf"), []byte("aaaa"), 0o644)
|
|
|
|
if _, err := f.Ensure(t.Context(), srv.URL+"/m/v/"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Resuming a set means not paying for the shards that already landed.
|
|
if *hits != 1 {
|
|
t.Errorf("expected only the missing file to be fetched, got %d downloads", *hits)
|
|
}
|
|
}
|
|
|
|
func TestAFilenameThatEscapesTheDirectoryIsRefused(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = w.Write([]byte(`{"files":[{"name":"../../etc/passwd","bytes":1}]}`))
|
|
}))
|
|
defer srv.Close()
|
|
f := &Fetcher{CacheDir: t.TempDir()}
|
|
|
|
_, err := f.Ensure(t.Context(), srv.URL+"/m/v/")
|
|
// The mirror is a place an operator copies files into by hand, so what it
|
|
// reports is not automatically safe to join onto a path.
|
|
if err == nil || !strings.Contains(err.Error(), "offered a file named") {
|
|
t.Errorf("expected the name to be refused, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestADirectoryListingIsNotAManifest(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = w.Write([]byte("<pre><a href=\"a.gguf\">a.gguf</a></pre>"))
|
|
}))
|
|
defer srv.Close()
|
|
f := &Fetcher{CacheDir: t.TempDir()}
|
|
|
|
_, err := f.Ensure(t.Context(), srv.URL+"/m/v/")
|
|
// "invalid character '<'" sends somebody looking at the wrong thing.
|
|
if err == nil || !strings.Contains(err.Error(), "has to be a mirror version") {
|
|
t.Errorf("the reason should name the cause, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOnlyATrailingSlashMeansASet(t *testing.T) {
|
|
for src, want := range map[string]bool{
|
|
"http://mirror/m/v/": true,
|
|
"http://mirror/m/v/model.gguf": false,
|
|
"/var/lib/models/": false, // a local directory is not fetched
|
|
"Qwen/Qwen2.5-7B-Instruct": false, // a repo id, resolved by the engine
|
|
} {
|
|
if got := IsSet(src); got != want {
|
|
t.Errorf("IsSet(%q) = %v, want %v", src, got, want)
|
|
}
|
|
}
|
|
}
|