Repository: ollama/ollama
Pull request: fix relative path on create

This fixes a regression in the API. Previously calling the API directly with a modelfile that has a relative file would work.

Ex:
```
FROM nous-capybara-34b.Q4_0.gguf
TEMPLATE "USER: { .Prompt } ASSISTANT: "
```

```
curl -X POST http://localhost:11434/api/create -d '{
    "name": "bruce/nous-capybara",
    "path": "/Users/bruce/models/nous-capybara/Modelfile"
}'
```

part of #1217

Changed files:
filename: server/images.go
patch: @@ -228,27 +228,32 @@ func GetModel(name string) (*Model, error) {
 	return model, nil
 }
 
-func realpath(p string) string {
-	abspath, err := filepath.Abs(p)
+func realpath(mfDir, from string) string {
+	abspath, err := filepath.Abs(from)
 	if err != nil {
-		return p
+		return from
 	}
 
 	home, err := os.UserHomeDir()
 	if err != nil {
 		return abspath
 	}
 
-	if p == "~" {
+	if from == "~" {
 		return home
-	} else if strings.HasPrefix(p, "~/") {
-		return filepath.Join(home, p[2:])
+	} else if strings.HasPrefix(from, "~/") {
+		return filepath.Join(home, from[2:])
+	}
+
+	if _, err := os.Stat(filepath.Join(mfDir, from)); err == nil {
+		// this is a file relative to the Modelfile
+		return filepath.Join(mfDir, from)
 	}
 
 	return abspath
 }
 
-func CreateModel(ctx context.Context, name string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
+func CreateModel(ctx context.Context, name, modelFileDir string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
 	config := ConfigV2{
 		OS:           "linux",
 		Architecture: "amd64",
@@ -276,7 +281,7 @@ func CreateModel(ctx context.Context, name string, commands []parser.Command, fn
 				c.Args = blobPath
 			}
 
-			bin, err := os.Open(realpath(c.Args))
+			bin, err := os.Open(realpath(modelFileDir, c.Args))
 			if err != nil {
 				// not a file on disk so must be a model reference
 				modelpath := ParseModelPath(c.Args)
@@ -372,7 +377,7 @@ func CreateModel(ctx context.Context, name string, commands []parser.Command, fn
 			layers = append(layers, layer)
 		case "adapter":
 			fn(api.ProgressResponse{Status: "creating adapter layer"})
-			bin, err := os.Open(realpath(c.Args))
+			bin, err := os.Open(realpath(modelFileDir, c.Args))
 			if err != nil {
 				return err
 			}

filename: server/routes.go
patch: @@ -423,14 +423,14 @@ func CreateModelHandler(c *gin.Context) {
 
 	var modelfile io.Reader = strings.NewReader(req.Modelfile)
 	if req.Path != "" && req.Modelfile == "" {
-		bin, err := os.Open(req.Path)
+		mf, err := os.Open(req.Path)
 		if err != nil {
 			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("error reading modelfile: %s", err)})
 			return
 		}
-		defer bin.Close()
+		defer mf.Close()
 
-		modelfile = bin
+		modelfile = mf
 	}
 
 	commands, err := parser.Parse(modelfile)
@@ -449,7 +449,7 @@ func CreateModelHandler(c *gin.Context) {
 		ctx, cancel := context.WithCancel(c.Request.Context())
 		defer cancel()
 
-		if err := CreateModel(ctx, req.Name, commands, fn); err != nil {
+		if err := CreateModel(ctx, req.Name, filepath.Dir(req.Path), commands, fn); err != nil {
 			ch <- gin.H{"error": err.Error()}
 		}
 	}()