mirror of
https://github.com/ollama/ollama.git
synced 2026-04-17 21:54:08 +02:00
modelfiles: fix /save command and add shortname for safetensors based models (#15413)
This change fixes two issues with Modelfiles:
1. If a user uses `ollama show --modelfile` to show a safetensors based
model, the Model would leave the "FROM" field blank which won't allow
a user to recreate the model. This change adds the model's current
canonical short name to the FROM field.
2. If a user uses the `/save` command in the CLI any messages which were
saved in a previous model wouldn't get saved (only the set of messages
from the current session).
This commit is contained in:
@@ -1308,9 +1308,17 @@ func GetModelInfo(req api.ShowRequest) (*api.ShowResponse, error) {
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintln(&sb, "# Modelfile generated by \"ollama show\"")
|
||||
fmt.Fprintln(&sb, "# To build a new Modelfile based on this, replace FROM with:")
|
||||
fmt.Fprintf(&sb, "# FROM %s\n\n", m.ShortName)
|
||||
fmt.Fprint(&sb, m.String())
|
||||
modelfile := m.String()
|
||||
if m.IsMLX() {
|
||||
fmt.Fprintf(&sb, "FROM %s\n", m.ShortName)
|
||||
if _, rest, ok := strings.Cut(modelfile, "\n"); ok {
|
||||
fmt.Fprint(&sb, rest)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(&sb, "# To build a new Modelfile based on this, replace FROM with:")
|
||||
fmt.Fprintf(&sb, "# FROM %s\n\n", m.ShortName)
|
||||
fmt.Fprint(&sb, modelfile)
|
||||
}
|
||||
resp.Modelfile = sb.String()
|
||||
|
||||
// skip loading tensor information if this is a remote model
|
||||
|
||||
@@ -580,6 +580,41 @@ func TestGetModelInfo_SafetensorsUsesStoredFileType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetModelInfo_SafetensorsModelfileUsesShortName(t *testing.T) {
|
||||
t.Setenv("OLLAMA_MODELS", t.TempDir())
|
||||
|
||||
cfgData, err := json.Marshal(model.ConfigV2{
|
||||
ModelFormat: "safetensors",
|
||||
Capabilities: []string{"completion"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal config: %v", err)
|
||||
}
|
||||
|
||||
configLayer, err := manifest.NewLayer(bytes.NewReader(cfgData), "application/vnd.docker.container.image.v1+json")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create config layer: %v", err)
|
||||
}
|
||||
|
||||
name := model.ParseName("show-safetensors")
|
||||
if err := manifest.WriteManifest(name, configLayer, nil); err != nil {
|
||||
t.Fatalf("failed to write manifest: %v", err)
|
||||
}
|
||||
|
||||
resp, err := GetModelInfo(api.ShowRequest{Model: name.String()})
|
||||
if err != nil {
|
||||
t.Fatalf("GetModelInfo() error = %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(resp.Modelfile, "FROM show-safetensors:latest\n") {
|
||||
t.Fatalf("Modelfile = %q, want FROM show-safetensors:latest", resp.Modelfile)
|
||||
}
|
||||
|
||||
if strings.Contains(resp.Modelfile, "# To build a new Modelfile based on this, replace FROM with:") {
|
||||
t.Fatalf("Modelfile should not include replacement hint: %q", resp.Modelfile)
|
||||
}
|
||||
}
|
||||
|
||||
func casingShuffle(s string) string {
|
||||
rr := []rune(s)
|
||||
for i := range rr {
|
||||
|
||||
Reference in New Issue
Block a user