diff --git a/server/create.go b/server/create.go index 19f24ec805..4fdf4104f8 100644 --- a/server/create.go +++ b/server/create.go @@ -119,6 +119,27 @@ func (s *Server) CreateHandler(c *gin.Context) { if err != nil { ch <- gin.H{"error": err.Error()} } + + if err == nil && !remote && (config.Renderer == "" || config.Parser == "") { + manifest, mErr := ParseNamedManifest(fromName) + if mErr == nil && manifest.Config.Digest != "" { + configPath, pErr := GetBlobsPath(manifest.Config.Digest) + if pErr == nil { + if cfgFile, fErr := os.Open(configPath); fErr == nil { + var baseConfig ConfigV2 + if decErr := json.NewDecoder(cfgFile).Decode(&baseConfig); decErr == nil { + if config.Renderer == "" { + config.Renderer = baseConfig.Renderer + } + if config.Parser == "" { + config.Parser = baseConfig.Parser + } + } + cfgFile.Close() + } + } + } + } } } else if r.Files != nil { baseLayers, err = convertModelFromFiles(r.Files, baseLayers, false, fn) diff --git a/server/routes_create_test.go b/server/routes_create_test.go index f4f7b76c10..909ebfe537 100644 --- a/server/routes_create_test.go +++ b/server/routes_create_test.go @@ -188,6 +188,72 @@ func TestCreateFromModel(t *testing.T) { }) } +func TestCreateFromModelInheritsRendererParser(t *testing.T) { + gin.SetMode(gin.TestMode) + + p := t.TempDir() + t.Setenv("OLLAMA_MODELS", p) + var s Server + + const ( + renderer = "custom-renderer" + parser = "custom-parser" + ) + + _, digest := createBinFile(t, nil, nil) + + w := createRequest(t, s.CreateHandler, api.CreateRequest{ + Name: "base", + Files: map[string]string{"base.gguf": digest}, + Renderer: renderer, + Parser: parser, + Stream: &stream, + }) + if w.Code != http.StatusOK { + t.Fatalf("expected status code 200, actual %d", w.Code) + } + + w = createRequest(t, s.CreateHandler, api.CreateRequest{ + Name: "child", + From: "base", + Stream: &stream, + }) + if w.Code != http.StatusOK { + t.Fatalf("expected status code 200, actual %d", w.Code) + } + + manifest, err := ParseNamedManifest(model.ParseName("child")) + if err != nil { + t.Fatalf("parse manifest: %v", err) + } + if manifest.Config.Digest == "" { + t.Fatalf("unexpected empty config digest for child manifest") + } + + configPath, err := GetBlobsPath(manifest.Config.Digest) + if err != nil { + t.Fatalf("config blob path: %v", err) + } + + cfgFile, err := os.Open(configPath) + if err != nil { + t.Fatalf("open config blob: %v", err) + } + defer cfgFile.Close() + + var cfg ConfigV2 + if err := json.NewDecoder(cfgFile).Decode(&cfg); err != nil { + t.Fatalf("decode config: %v", err) + } + + if cfg.Renderer != renderer { + t.Fatalf("expected renderer %q, got %q", renderer, cfg.Renderer) + } + if cfg.Parser != parser { + t.Fatalf("expected parser %q, got %q", parser, cfg.Parser) + } +} + func TestCreateRemovesLayers(t *testing.T) { gin.SetMode(gin.TestMode)