mirror of
https://github.com/ollama/ollama.git
synced 2025-11-11 13:07:34 +01:00
create: inherit FROM model's renderer/parser
On main, the `RENDERER` and `PARSER` fields from the `Modelfile` don't get propagated to a new model created with a `req.From` parameter. This is easily triggered via `ollama run qwen3-coder`, then running some save command like `/save qwen3-coder-custom`. Added a regression test for this, and then open the config for the "from" model in order to use its renderer/parser as a default for the new model. This will fix the CLI and also API-based creates. Fixes: https://github.com/ollama/ollama/issues/12792
This commit is contained in:
@@ -119,6 +119,27 @@ func (s *Server) CreateHandler(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
ch <- gin.H{"error": err.Error()}
|
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 {
|
} else if r.Files != nil {
|
||||||
baseLayers, err = convertModelFromFiles(r.Files, baseLayers, false, fn)
|
baseLayers, err = convertModelFromFiles(r.Files, baseLayers, false, fn)
|
||||||
|
|||||||
@@ -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) {
|
func TestCreateRemovesLayers(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user