images.go 26.6 KB
Newer Older
1
2
3
4
package server

import (
	"bytes"
5
	"context"
6
	"crypto/sha256"
Patrick Devine's avatar
Patrick Devine committed
7
	"encoding/hex"
8
9
10
11
12
13
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
Michael Yang's avatar
Michael Yang committed
14
	"net/url"
15
16
	"os"
	"path/filepath"
Michael Yang's avatar
Michael Yang committed
17
	"runtime"
18
19
	"strconv"
	"strings"
Quinn Slack's avatar
Quinn Slack committed
20
	"text/template"
21

Michael Yang's avatar
Michael Yang committed
22
23
	"golang.org/x/exp/slices"

24
	"github.com/jmorganca/ollama/api"
25
	"github.com/jmorganca/ollama/llm"
26
	"github.com/jmorganca/ollama/parser"
Michael Yang's avatar
Michael Yang committed
27
	"github.com/jmorganca/ollama/version"
28
29
)

30
31
32
33
type RegistryOptions struct {
	Insecure bool
	Username string
	Password string
Patrick Devine's avatar
Patrick Devine committed
34
	Token    string
35
36
}

37
type Model struct {
Michael Yang's avatar
Michael Yang committed
38
	Name           string `json:"name"`
39
	Config         ConfigV2
Michael Yang's avatar
Michael Yang committed
40
41
42
43
44
45
46
47
48
49
	ShortName      string
	ModelPath      string
	OriginalModel  string
	AdapterPaths   []string
	ProjectorPaths []string
	Template       string
	System         string
	License        []string
	Digest         string
	Options        map[string]interface{}
50
51
}

Bruce MacDonald's avatar
Bruce MacDonald committed
52
53
54
55
56
57
type PromptVars struct {
	System   string
	Prompt   string
	Response string
	First    bool
}
58

Bruce MacDonald's avatar
Bruce MacDonald committed
59
60
61
func (m *Model) Prompt(p PromptVars) (string, error) {
	var prompt strings.Builder
	tmpl, err := template.New("").Parse(m.Template)
62
63
64
65
	if err != nil {
		return "", err
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
66
67
68
	if p.System == "" {
		// use the default system prompt for this model if one is not specified
		p.System = m.System
69
70
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
71
72
73
74
75
76
77
78
	var sb strings.Builder
	if err := tmpl.Execute(&sb, p); err != nil {
		return "", err
	}
	prompt.WriteString(sb.String())
	prompt.WriteString(p.Response)
	return prompt.String(), nil
}
79

Bruce MacDonald's avatar
Bruce MacDonald committed
80
81
82
83
84
func (m *Model) ChatPrompt(msgs []api.Message) (string, error) {
	// build the prompt from the list of messages
	var prompt strings.Builder
	currentVars := PromptVars{
		First: true,
Bruce MacDonald's avatar
Bruce MacDonald committed
85
86
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	writePrompt := func() error {
		p, err := m.Prompt(currentVars)
		if err != nil {
			return err
		}
		prompt.WriteString(p)
		currentVars = PromptVars{}
		return nil
	}

	for _, msg := range msgs {
		switch msg.Role {
		case "system":
			if currentVars.Prompt != "" || currentVars.System != "" {
				if err := writePrompt(); err != nil {
					return "", err
				}
			}
			currentVars.System = msg.Content
		case "user":
			if currentVars.Prompt != "" || currentVars.System != "" {
				if err := writePrompt(); err != nil {
					return "", err
				}
			}
			currentVars.Prompt = msg.Content
		case "assistant":
			currentVars.Response = msg.Content
			if err := writePrompt(); err != nil {
				return "", err
			}
		default:
			return "", fmt.Errorf("invalid role: %s, role must be one of [system, user, assistant]", msg.Role)
		}
121
122
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
123
124
125
126
127
128
129
130
	// Append the last set of vars if they are non-empty
	if currentVars.Prompt != "" || currentVars.System != "" {
		if err := writePrompt(); err != nil {
			return "", err
		}
	}

	return prompt.String(), nil
131
132
}

133
134
135
type ManifestV2 struct {
	SchemaVersion int      `json:"schemaVersion"`
	MediaType     string   `json:"mediaType"`
Michael Yang's avatar
Michael Yang committed
136
	Config        *Layer   `json:"config"`
137
138
139
140
	Layers        []*Layer `json:"layers"`
}

type ConfigV2 struct {
141
	// required by spec
142
143
	Architecture string `json:"architecture"`
	OS           string `json:"os"`
144
145
146
	RootFS       RootFS `json:"rootfs"`

	api.ModelConfiguration
147
148
}

Michael Yang's avatar
Michael Yang committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
func (c *ConfigV2) SetModelFormat(format string) {
	if c.ModelFormat == "" {
		c.ModelFormat = format
	}
}

func (c *ConfigV2) SetModelFamily(families ...string) {
	for _, family := range families {
		if c.ModelFamily == "" {
			c.ModelFamily = family
		}

		if !slices.Contains(c.ModelFamilies, family) {
			c.ModelFamilies = append(c.ModelFamilies, family)
		}
	}
}

func (c *ConfigV2) SetModelType(modelType string) {
	if c.ModelType == "" {
		c.ModelType = modelType
	}
}

func (c *ConfigV2) SetFileType(fileType string) {
	if c.FileType == "" {
		c.FileType = fileType
	}
}

179
180
181
182
183
type RootFS struct {
	Type    string   `json:"type"`
	DiffIDs []string `json:"diff_ids"`
}

Michael Yang's avatar
Michael Yang committed
184
func (m *ManifestV2) GetTotalSize() (total int64) {
Patrick Devine's avatar
Patrick Devine committed
185
186
187
	for _, layer := range m.Layers {
		total += layer.Size
	}
Michael Yang's avatar
Michael Yang committed
188

Patrick Devine's avatar
Patrick Devine committed
189
190
191
192
	total += m.Config.Size
	return total
}

Patrick Devine's avatar
Patrick Devine committed
193
func GetManifest(mp ModelPath) (*ManifestV2, string, error) {
194
	fp, err := mp.GetManifestPath()
195
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
196
		return nil, "", err
197
	}
198

199
	if _, err = os.Stat(fp); err != nil {
Patrick Devine's avatar
Patrick Devine committed
200
		return nil, "", err
201
202
203
204
	}

	var manifest *ManifestV2

205
	bts, err := os.ReadFile(fp)
206
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
207
		return nil, "", fmt.Errorf("couldn't open file '%s'", fp)
208
209
	}

Patrick Devine's avatar
Patrick Devine committed
210
211
212
	shaSum := sha256.Sum256(bts)
	shaStr := hex.EncodeToString(shaSum[:])

213
	if err := json.Unmarshal(bts, &manifest); err != nil {
Patrick Devine's avatar
Patrick Devine committed
214
		return nil, "", err
215
216
	}

Patrick Devine's avatar
Patrick Devine committed
217
	return manifest, shaStr, nil
218
219
220
}

func GetModel(name string) (*Model, error) {
221
	mp := ParseModelPath(name)
Patrick Devine's avatar
Patrick Devine committed
222
	manifest, digest, err := GetManifest(mp)
223
224
225
226
227
	if err != nil {
		return nil, err
	}

	model := &Model{
228
229
230
231
232
		Name:      mp.GetFullTagname(),
		ShortName: mp.GetShortTagname(),
		Digest:    digest,
		Template:  "{{ .Prompt }}",
		License:   []string{},
233
234
	}

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
	filename, err := GetBlobsPath(manifest.Config.Digest)
	if err != nil {
		return nil, err
	}

	configFile, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer configFile.Close()

	if err := json.NewDecoder(configFile).Decode(&model.Config); err != nil {
		return nil, err
	}

250
	for _, layer := range manifest.Layers {
Patrick Devine's avatar
Patrick Devine committed
251
		filename, err := GetBlobsPath(layer.Digest)
252
253
254
255
		if err != nil {
			return nil, err
		}

256
257
258
		switch layer.MediaType {
		case "application/vnd.ollama.image.model":
			model.ModelPath = filename
Patrick Devine's avatar
Patrick Devine committed
259
			model.OriginalModel = layer.From
260
		case "application/vnd.ollama.image.embed":
261
262
263
			// Deprecated in versions  > 0.1.2
			// TODO: remove this warning in a future version
			log.Print("WARNING: model contains embeddings, but embeddings in modelfiles have been deprecated and will be ignored.")
264
265
		case "application/vnd.ollama.image.adapter":
			model.AdapterPaths = append(model.AdapterPaths, filename)
Michael Yang's avatar
Michael Yang committed
266
267
		case "application/vnd.ollama.image.projector":
			model.ProjectorPaths = append(model.ProjectorPaths, filename)
268
269
270
271
272
273
274
275
276
		case "application/vnd.ollama.image.template":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}

			model.Template = string(bts)
		case "application/vnd.ollama.image.system":
			bts, err := os.ReadFile(filename)
277
278
279
			if err != nil {
				return nil, err
			}
280
281

			model.System = string(bts)
282
283
284
285
286
287
288
		case "application/vnd.ollama.image.prompt":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}

			model.Template = string(bts)
289
		case "application/vnd.ollama.image.params":
Michael Yang's avatar
Michael Yang committed
290
291
292
293
294
			params, err := os.Open(filename)
			if err != nil {
				return nil, err
			}
			defer params.Close()
295

296
			// parse model options parameters into a map so that we can see which fields have been specified explicitly
297
			if err = json.NewDecoder(params).Decode(&model.Options); err != nil {
298
299
				return nil, err
			}
Patrick Devine's avatar
Patrick Devine committed
300
301
302
303
304
305
		case "application/vnd.ollama.image.license":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}
			model.License = append(model.License, string(bts))
306
307
308
309
310
311
		}
	}

	return model, nil
}

312
313
func realpath(mfDir, from string) string {
	abspath, err := filepath.Abs(from)
Michael Yang's avatar
Michael Yang committed
314
	if err != nil {
315
		return from
316
317
	}

Michael Yang's avatar
Michael Yang committed
318
	home, err := os.UserHomeDir()
319
	if err != nil {
Michael Yang's avatar
Michael Yang committed
320
		return abspath
321
322
	}

323
	if from == "~" {
Michael Yang's avatar
Michael Yang committed
324
		return home
325
326
327
328
329
330
331
	} 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)
332
333
	}

Michael Yang's avatar
Michael Yang committed
334
335
336
	return abspath
}

337
func CreateModel(ctx context.Context, name, modelFileDir string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
338
339
	config := ConfigV2{
		OS:           "linux",
Michael Yang's avatar
Michael Yang committed
340
		Architecture: "amd64",
Michael Yang's avatar
Michael Yang committed
341
342
343
		RootFS: RootFS{
			Type: "layers",
		},
344
345
	}

Michael Yang's avatar
Michael Yang committed
346
347
	deleteMap := make(map[string]struct{})

Michael Yang's avatar
Michael Yang committed
348
	var layers Layers
Michael Yang's avatar
Michael Yang committed
349

350
	params := make(map[string][]string)
Michael Yang's avatar
Michael Yang committed
351
352
	fromParams := make(map[string]any)

353
	for _, c := range commands {
Michael Yang's avatar
Michael Yang committed
354
355
356
		log.Printf("[%s] - %s", c.Name, c.Args)
		mediatype := fmt.Sprintf("application/vnd.ollama.image.%s", c.Name)

357
358
		switch c.Name {
		case "model":
Michael Yang's avatar
Michael Yang committed
359
360
361
362
363
364
365
366
367
			if strings.HasPrefix(c.Args, "@") {
				blobPath, err := GetBlobsPath(strings.TrimPrefix(c.Args, "@"))
				if err != nil {
					return err
				}

				c.Args = blobPath
			}

368
			bin, err := os.Open(realpath(modelFileDir, c.Args))
369
			if err != nil {
Michael Yang's avatar
Michael Yang committed
370
371
372
373
374
375
376
				// not a file on disk so must be a model reference
				modelpath := ParseModelPath(c.Args)
				manifest, _, err := GetManifest(modelpath)
				switch {
				case errors.Is(err, os.ErrNotExist):
					fn(api.ProgressResponse{Status: "pulling model"})
					if err := PullModel(ctx, c.Args, &RegistryOptions{}, fn); err != nil {
377
378
379
						return err
					}

Michael Yang's avatar
Michael Yang committed
380
					manifest, _, err = GetManifest(modelpath)
381
382
383
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
384
385
				case err != nil:
					return err
386
				}
387

388
				fn(api.ProgressResponse{Status: "reading model metadata"})
Michael Yang's avatar
Michael Yang committed
389
				fromConfigPath, err := GetBlobsPath(manifest.Config.Digest)
Michael Yang's avatar
Michael Yang committed
390
391
392
393
				if err != nil {
					return err
				}

Michael Yang's avatar
Michael Yang committed
394
				fromConfigFile, err := os.Open(fromConfigPath)
Michael Yang's avatar
Michael Yang committed
395
396
397
				if err != nil {
					return err
				}
Michael Yang's avatar
Michael Yang committed
398
				defer fromConfigFile.Close()
Michael Yang's avatar
Michael Yang committed
399

Michael Yang's avatar
Michael Yang committed
400
401
				var fromConfig ConfigV2
				if err := json.NewDecoder(fromConfigFile).Decode(&fromConfig); err != nil {
Michael Yang's avatar
Michael Yang committed
402
403
404
					return err
				}

Michael Yang's avatar
Michael Yang committed
405
406
407
408
				config.SetModelFormat(fromConfig.ModelFormat)
				config.SetModelFamily(append(fromConfig.ModelFamilies, fromConfig.ModelFamily)...)
				config.SetModelType(fromConfig.ModelType)
				config.SetFileType(fromConfig.FileType)
Michael Yang's avatar
Michael Yang committed
409

Michael Yang's avatar
Michael Yang committed
410
411
412
413
				for _, layer := range manifest.Layers {
					deleteMap[layer.Digest] = struct{}{}
					if layer.MediaType == "application/vnd.ollama.image.params" {
						fromParamsPath, err := GetBlobsPath(layer.Digest)
Michael Yang's avatar
Michael Yang committed
414
415
416
417
						if err != nil {
							return err
						}

Michael Yang's avatar
Michael Yang committed
418
						fromParamsFile, err := os.Open(fromParamsPath)
Michael Yang's avatar
Michael Yang committed
419
420
421
						if err != nil {
							return err
						}
Michael Yang's avatar
Michael Yang committed
422
						defer fromParamsFile.Close()
Michael Yang's avatar
Michael Yang committed
423

Michael Yang's avatar
Michael Yang committed
424
						if err := json.NewDecoder(fromParamsFile).Decode(&fromParams); err != nil {
Michael Yang's avatar
Michael Yang committed
425
426
427
428
							return err
						}
					}

Michael Yang's avatar
Michael Yang committed
429
					layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, modelpath.GetShortTagname())
430
431
432
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
433

Michael Yang's avatar
Michael Yang committed
434
					layers.Add(layer)
435
				}
Michael Yang's avatar
Michael Yang committed
436
437
438

				deleteMap[manifest.Config.Digest] = struct{}{}
				continue
439
			}
Michael Yang's avatar
Michael Yang committed
440
			defer bin.Close()
441

442
443
444
			var offset int64
			for {
				fn(api.ProgressResponse{Status: "creating model layer"})
445

446
447
448
449
450
451
452
				bin.Seek(offset, io.SeekStart)
				ggml, err := llm.DecodeGGML(bin)
				if errors.Is(err, io.EOF) {
					break
				} else if err != nil {
					return err
				}
Michael Yang's avatar
Michael Yang committed
453

Michael Yang's avatar
Michael Yang committed
454
455
456
457
				config.SetModelFormat(ggml.Name())
				config.SetModelFamily(ggml.ModelFamily())
				config.SetModelType(ggml.ModelType())
				config.SetFileType(ggml.FileType())
458

459
460
461
462
				mediatype := mediatype
				if ggml.ModelFamily() == "clip" {
					mediatype = "application/vnd.ollama.image.projector"
				}
463

464
465
466
467
468
469
470
471
472
473
				sr := io.NewSectionReader(bin, offset, ggml.Size)
				layer, err := NewLayer(sr, mediatype)
				if err != nil {
					return err
				}

				layers.Add(layer)

				offset += ggml.Size
			}
Michael Yang's avatar
Michael Yang committed
474
		case "adapter":
475
476
477
478
479
480
481
482
			if strings.HasPrefix(c.Args, "@") {
				blobPath, err := GetBlobsPath(strings.TrimPrefix(c.Args, "@"))
				if err != nil {
					return err
				}

				c.Args = blobPath
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
483

Michael Yang's avatar
Michael Yang committed
484
			fn(api.ProgressResponse{Status: "creating adapter layer"})
485
			bin, err := os.Open(realpath(modelFileDir, c.Args))
486
			if err != nil {
Michael Yang's avatar
Michael Yang committed
487
				return err
488
			}
Michael Yang's avatar
Michael Yang committed
489
			defer bin.Close()
490

Michael Yang's avatar
Michael Yang committed
491
			layer, err := NewLayer(bin, mediatype)
492
			if err != nil {
Michael Yang's avatar
Michael Yang committed
493
				return err
494
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
495

Michael Yang's avatar
Michael Yang committed
496
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
497
498
		case "license":
			fn(api.ProgressResponse{Status: "creating license layer"})
Michael Yang's avatar
Michael Yang committed
499
500
501

			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
Bruce MacDonald's avatar
Bruce MacDonald committed
502
503
504
505
			if err != nil {
				return err
			}

Michael Yang's avatar
Michael Yang committed
506
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
507
508
509
		case "template", "system":
			fn(api.ProgressResponse{Status: fmt.Sprintf("creating %s layer", c.Name)})

Michael Yang's avatar
Michael Yang committed
510
511
			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
512
			if err != nil {
513
				return err
514
			}
515

Michael Yang's avatar
Michael Yang committed
516
			layers.Replace(layer)
517
		default:
518
			params[c.Name] = append(params[c.Name], c.Args)
519
520
521
		}
	}

Michael Yang's avatar
Michael Yang committed
522
	if len(params) > 0 {
Michael Yang's avatar
Michael Yang committed
523
		fn(api.ProgressResponse{Status: "creating parameters layer"})
Michael Yang's avatar
Michael Yang committed
524

525
		formattedParams, err := api.FormatParams(params)
526
		if err != nil {
Michael Yang's avatar
Michael Yang committed
527
			return err
528
		}
529

Michael Yang's avatar
Michael Yang committed
530
		for k, v := range fromParams {
Michael Yang's avatar
Michael Yang committed
531
532
533
534
535
			if _, ok := formattedParams[k]; !ok {
				formattedParams[k] = v
			}
		}

Michael Yang's avatar
Michael Yang committed
536
		if config.ModelType == "65B" {
Michael Yang's avatar
Michael Yang committed
537
			if gqa, ok := formattedParams["gqa"].(int); ok && gqa == 8 {
Michael Yang's avatar
Michael Yang committed
538
539
540
541
				config.ModelType = "70B"
			}
		}

Michael Yang's avatar
Michael Yang committed
542
543
		var b bytes.Buffer
		if err := json.NewEncoder(&b).Encode(formattedParams); err != nil {
544
545
546
			return err
		}

Michael Yang's avatar
Michael Yang committed
547
		fn(api.ProgressResponse{Status: "creating config layer"})
Michael Yang's avatar
Michael Yang committed
548
		layer, err := NewLayer(&b, "application/vnd.ollama.image.params")
549
		if err != nil {
Michael Yang's avatar
Michael Yang committed
550
			return err
551
		}
Michael Yang's avatar
Michael Yang committed
552

Michael Yang's avatar
Michael Yang committed
553
		layers.Replace(layer)
554
555
	}

Michael Yang's avatar
Michael Yang committed
556
557
558
	digests := make([]string, len(layers.items))
	for i, layer := range layers.items {
		digests[i] = layer.Digest
559
560
	}

Michael Yang's avatar
Michael Yang committed
561
	config.RootFS.DiffIDs = digests
Michael Yang's avatar
Michael Yang committed
562

Michael Yang's avatar
Michael Yang committed
563
564
	var b bytes.Buffer
	if err := json.NewEncoder(&b).Encode(config); err != nil {
565
566
567
		return err
	}

Michael Yang's avatar
Michael Yang committed
568
569
	configLayer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json")
	if err != nil {
570
571
572
		return err
	}

Michael Yang's avatar
Michael Yang committed
573
	delete(deleteMap, configLayer.Digest)
574

Michael Yang's avatar
Michael Yang committed
575
576
	for _, layer := range append(layers.items, configLayer) {
		committed, err := layer.Commit()
577
578
579
		if err != nil {
			return err
		}
580

Michael Yang's avatar
Michael Yang committed
581
582
583
		status := "writing layer"
		if !committed {
			status = "using already created layer"
584
585
		}

Michael Yang's avatar
Michael Yang committed
586
		fn(api.ProgressResponse{Status: fmt.Sprintf("%s %s", status, layer.Digest)})
587

Michael Yang's avatar
Michael Yang committed
588
		delete(deleteMap, layer.Digest)
589
590
	}

Michael Yang's avatar
Michael Yang committed
591
592
	fn(api.ProgressResponse{Status: "writing manifest"})
	if err := WriteManifest(name, configLayer, layers.items); err != nil {
593
594
		return err
	}
595

Michael Yang's avatar
Michael Yang committed
596
597
598
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := deleteUnusedLayers(nil, deleteMap, false); err != nil {
			return err
599
600
601
		}
	}

Michael Yang's avatar
Michael Yang committed
602
603
	fn(api.ProgressResponse{Status: "success"})
	return nil
604
605
}

Patrick Devine's avatar
Patrick Devine committed
606
func CopyModel(src, dest string) error {
607
	srcModelPath := ParseModelPath(src)
608
	srcPath, err := srcModelPath.GetManifestPath()
609
610
611
612
	if err != nil {
		return err
	}

613
	destModelPath := ParseModelPath(dest)
614
	destPath, err := destModelPath.GetManifestPath()
Patrick Devine's avatar
Patrick Devine committed
615
616
617
	if err != nil {
		return err
	}
618
619
620
	if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
621
622

	// copy the file
Michael Yang's avatar
Michael Yang committed
623
	input, err := os.ReadFile(srcPath)
Patrick Devine's avatar
Patrick Devine committed
624
625
626
627
628
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

Michael Yang's avatar
Michael Yang committed
629
	err = os.WriteFile(destPath, input, 0o644)
Patrick Devine's avatar
Patrick Devine committed
630
631
632
633
634
635
636
637
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

	return nil
}

Michael Yang's avatar
Michael Yang committed
638
func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}, dryRun bool) error {
639
640
641
642
	fp, err := GetManifestPath()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
643
644
645
646

	walkFunc := func(path string, info os.FileInfo, _ error) error {
		if info.IsDir() {
			return nil
647
648
		}

Michael Yang's avatar
Michael Yang committed
649
650
651
652
		dir, file := filepath.Split(path)
		dir = strings.Trim(strings.TrimPrefix(dir, fp), string(os.PathSeparator))
		tag := strings.Join([]string{dir, file}, ":")
		fmp := ParseModelPath(tag)
653

Michael Yang's avatar
Michael Yang committed
654
		// skip the manifest we're trying to delete
655
		if skipModelPath != nil && skipModelPath.GetFullTagname() == fmp.GetFullTagname() {
Michael Yang's avatar
Michael Yang committed
656
			return nil
657
		}
Michael Yang's avatar
Michael Yang committed
658
659
660
661
662
663
664
665
666
667
668
669

		// save (i.e. delete from the deleteMap) any files used in other manifests
		manifest, _, err := GetManifest(fmp)
		if err != nil {
			return nil
		}

		for _, layer := range manifest.Layers {
			delete(deleteMap, layer.Digest)
		}

		delete(deleteMap, manifest.Config.Digest)
670
		return nil
Michael Yang's avatar
Michael Yang committed
671
672
673
	}

	if err := filepath.Walk(fp, walkFunc); err != nil {
Michael Yang's avatar
Michael Yang committed
674
675
		return err
	}
676
677

	// only delete the files which are still in the deleteMap
Michael Yang's avatar
Michael Yang committed
678
679
680
681
682
683
684
685
686
	for k := range deleteMap {
		fp, err := GetBlobsPath(k)
		if err != nil {
			log.Printf("couldn't get file path for '%s': %v", k, err)
			continue
		}
		if !dryRun {
			if err := os.Remove(fp); err != nil {
				log.Printf("couldn't remove file '%s': %v", fp, err)
687
688
				continue
			}
Michael Yang's avatar
Michael Yang committed
689
690
		} else {
			log.Printf("wanted to remove: %s", fp)
691
692
693
		}
	}

694
695
696
697
	return nil
}

func PruneLayers() error {
Michael Yang's avatar
Michael Yang committed
698
	deleteMap := make(map[string]struct{})
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
	p, err := GetBlobsPath("")
	if err != nil {
		return err
	}

	blobs, err := os.ReadDir(p)
	if err != nil {
		log.Printf("couldn't read dir '%s': %v", p, err)
		return err
	}

	for _, blob := range blobs {
		name := blob.Name()
		if runtime.GOOS == "windows" {
			name = strings.ReplaceAll(name, "-", ":")
		}
Michael Yang's avatar
Michael Yang committed
715
716
717
		if strings.HasPrefix(name, "sha256:") {
			deleteMap[name] = struct{}{}
		}
718
719
720
721
722
723
724
725
726
727
728
729
730
731
	}

	log.Printf("total blobs: %d", len(deleteMap))

	err = deleteUnusedLayers(nil, deleteMap, false)
	if err != nil {
		return err
	}

	log.Printf("total unused blobs removed: %d", len(deleteMap))

	return nil
}

Michael Yang's avatar
Michael Yang committed
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
func PruneDirectory(path string) error {
	info, err := os.Lstat(path)
	if err != nil {
		return err
	}

	if info.IsDir() && info.Mode()&os.ModeSymlink == 0 {
		entries, err := os.ReadDir(path)
		if err != nil {
			return err
		}

		for _, entry := range entries {
			if err := PruneDirectory(filepath.Join(path, entry.Name())); err != nil {
				return err
			}
		}

		entries, err = os.ReadDir(path)
		if err != nil {
			return err
		}

		if len(entries) > 0 {
			return nil
		}

		return os.Remove(path)
	}

	return nil
}

765
766
767
768
769
770
771
func DeleteModel(name string) error {
	mp := ParseModelPath(name)
	manifest, _, err := GetManifest(mp)
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
772
	deleteMap := make(map[string]struct{})
773
	for _, layer := range manifest.Layers {
Michael Yang's avatar
Michael Yang committed
774
		deleteMap[layer.Digest] = struct{}{}
775
	}
Michael Yang's avatar
Michael Yang committed
776
	deleteMap[manifest.Config.Digest] = struct{}{}
777
778
779
780
781
782

	err = deleteUnusedLayers(&mp, deleteMap, false)
	if err != nil {
		return err
	}

783
	fp, err := mp.GetManifestPath()
784
785
786
787
788
789
790
791
792
793
794
795
	if err != nil {
		return err
	}
	err = os.Remove(fp)
	if err != nil {
		log.Printf("couldn't remove manifest file '%s': %v", fp, err)
		return err
	}

	return nil
}

Patrick Devine's avatar
Patrick Devine committed
796
func ShowModelfile(model *Model) (string, error) {
Michael Yang's avatar
Michael Yang committed
797
	var mt struct {
Patrick Devine's avatar
Patrick Devine committed
798
		*Model
Michael Yang's avatar
Michael Yang committed
799
		From       string
Michael Yang's avatar
Michael Yang committed
800
		Parameters map[string][]any
Patrick Devine's avatar
Patrick Devine committed
801
802
	}

Michael Yang's avatar
Michael Yang committed
803
	mt.Parameters = make(map[string][]any)
Patrick Devine's avatar
Patrick Devine committed
804
	for k, v := range model.Options {
Michael Yang's avatar
Michael Yang committed
805
806
807
		if s, ok := v.([]any); ok {
			mt.Parameters[k] = s
			continue
Patrick Devine's avatar
Patrick Devine committed
808
809
		}

Michael Yang's avatar
Michael Yang committed
810
		mt.Parameters[k] = []any{v}
Patrick Devine's avatar
Patrick Devine committed
811
812
	}

Michael Yang's avatar
Michael Yang committed
813
814
	mt.Model = model
	mt.From = model.ModelPath
Patrick Devine's avatar
Patrick Devine committed
815

Michael Yang's avatar
Michael Yang committed
816
	if model.OriginalModel != "" {
Daniel Reis's avatar
Daniel Reis committed
817
		mt.From = model.OriginalModel
Patrick Devine's avatar
Patrick Devine committed
818
819
820
821
822
823
824
825
	}

	modelFile := `# Modelfile generated by "ollama show"
# To build a new Modelfile based on this one, replace the FROM line with:
# FROM {{ .ShortName }}

FROM {{ .From }}
TEMPLATE """{{ .Template }}"""
826
827

{{- if .System }}
Patrick Devine's avatar
Patrick Devine committed
828
SYSTEM """{{ .System }}"""
829
{{- end }}
830
831
832
833

{{- range $adapter := .AdapterPaths }}
ADAPTER {{ $adapter }}
{{- end }}
Michael Yang's avatar
Michael Yang committed
834

Michael Yang's avatar
Michael Yang committed
835
836
837
838
{{- range $k, $v := .Parameters }}
{{- range $parameter := $v }}
PARAMETER {{ $k }} {{ printf "%#v" $parameter }}
{{- end }}
Michael Yang's avatar
Michael Yang committed
839
{{- end }}`
Patrick Devine's avatar
Patrick Devine committed
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856

	tmpl, err := template.New("").Parse(modelFile)
	if err != nil {
		log.Printf("error parsing template: %q", err)
		return "", err
	}

	var buf bytes.Buffer

	if err = tmpl.Execute(&buf, mt); err != nil {
		log.Printf("error executing template: %q", err)
		return "", err
	}

	return buf.String(), nil
}

857
func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
858
	mp := ParseModelPath(name)
859
860
	fn(api.ProgressResponse{Status: "retrieving manifest"})

861
862
863
864
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
865
	manifest, _, err := GetManifest(mp)
866
	if err != nil {
867
		fn(api.ProgressResponse{Status: "couldn't retrieve manifest"})
868
869
870
871
		return err
	}

	var layers []*Layer
Jeffrey Morgan's avatar
Jeffrey Morgan committed
872
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
873
	layers = append(layers, manifest.Config)
874
875

	for _, layer := range layers {
Michael Yang's avatar
Michael Yang committed
876
		if err := uploadBlob(ctx, mp, layer, regOpts, fn); err != nil {
877
			log.Printf("error uploading blob: %v", err)
878
879
880
			if errors.Is(err, errUnauthorized) {
				return fmt.Errorf("unable to push %s, make sure this namespace exists and you are authorized to push to it", ParseModelPath(name).GetNamespaceRepository())
			}
881
882
			return err
		}
883
884
	}

885
	fn(api.ProgressResponse{Status: "pushing manifest"})
Michael Yang's avatar
Michael Yang committed
886
887
	requestURL := mp.BaseURL()
	requestURL = requestURL.JoinPath("v2", mp.GetNamespaceRepository(), "manifests", mp.Tag)
888
889
890
891
892
893

	manifestJSON, err := json.Marshal(manifest)
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
894
895
	headers := make(http.Header)
	headers.Set("Content-Type", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
896
	resp, err := makeRequestWithRetry(ctx, http.MethodPut, requestURL, headers, bytes.NewReader(manifestJSON), regOpts)
897
898
899
900
901
	if err != nil {
		return err
	}
	defer resp.Body.Close()

902
	fn(api.ProgressResponse{Status: "success"})
903
904
905
906

	return nil
}

907
func PullModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
908
909
	mp := ParseModelPath(name)

910
911
912
913
914
	var manifest *ManifestV2
	var err error
	var noprune string

	// build deleteMap to prune unused layers
Michael Yang's avatar
Michael Yang committed
915
	deleteMap := make(map[string]struct{})
916
917
918
919
920
921
922
923
924

	if noprune = os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		manifest, _, err = GetManifest(mp)
		if err != nil && !errors.Is(err, os.ErrNotExist) {
			return err
		}

		if manifest != nil {
			for _, l := range manifest.Layers {
Michael Yang's avatar
Michael Yang committed
925
				deleteMap[l.Digest] = struct{}{}
926
			}
Michael Yang's avatar
Michael Yang committed
927
			deleteMap[manifest.Config.Digest] = struct{}{}
928
929
930
		}
	}

931
932
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
933
	}
934

935
	fn(api.ProgressResponse{Status: "pulling manifest"})
936

937
	manifest, err = pullModelManifest(ctx, mp, regOpts)
938
	if err != nil {
939
		return fmt.Errorf("pull model manifest: %s", err)
940
941
942
	}

	var layers []*Layer
Bruce MacDonald's avatar
Bruce MacDonald committed
943
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
944
	layers = append(layers, manifest.Config)
945
946

	for _, layer := range layers {
947
948
949
950
951
952
953
954
		if err := downloadBlob(
			ctx,
			downloadOpts{
				mp:      mp,
				digest:  layer.Digest,
				regOpts: regOpts,
				fn:      fn,
			}); err != nil {
955
956
			return err
		}
957
		delete(deleteMap, layer.Digest)
958
	}
959
	delete(deleteMap, manifest.Config.Digest)
960

Michael Yang's avatar
Michael Yang committed
961
962
963
	fn(api.ProgressResponse{Status: "verifying sha256 digest"})
	for _, layer := range layers {
		if err := verifyBlob(layer.Digest); err != nil {
964
965
966
967
968
969
970
971
972
973
974
			if errors.Is(err, errDigestMismatch) {
				// something went wrong, delete the blob
				fp, err := GetBlobsPath(layer.Digest)
				if err != nil {
					return err
				}
				if err := os.Remove(fp); err != nil {
					// log this, but return the original error
					log.Printf("couldn't remove file with digest mismatch '%s': %v", fp, err)
				}
			}
Michael Yang's avatar
Michael Yang committed
975
976
977
978
			return err
		}
	}

979
	fn(api.ProgressResponse{Status: "writing manifest"})
980

981
	manifestJSON, err := json.Marshal(manifest)
982
983
984
985
	if err != nil {
		return err
	}

986
	fp, err := mp.GetManifestPath()
987
988
989
	if err != nil {
		return err
	}
990
991
992
	if err := os.MkdirAll(filepath.Dir(fp), 0o755); err != nil {
		return err
	}
993

Bruce MacDonald's avatar
Bruce MacDonald committed
994
	err = os.WriteFile(fp, manifestJSON, 0o644)
995
996
997
998
999
	if err != nil {
		log.Printf("couldn't write to %s", fp)
		return err
	}

1000
1001
1002
1003
1004
1005
1006
1007
	if noprune == "" {
		fn(api.ProgressResponse{Status: "removing any unused layers"})
		err = deleteUnusedLayers(nil, deleteMap, false)
		if err != nil {
			return err
		}
	}

1008
	fn(api.ProgressResponse{Status: "success"})
1009
1010
1011
1012

	return nil
}

1013
func pullModelManifest(ctx context.Context, mp ModelPath, regOpts *RegistryOptions) (*ManifestV2, error) {
Michael Yang's avatar
Michael Yang committed
1014
	requestURL := mp.BaseURL().JoinPath("v2", mp.GetNamespaceRepository(), "manifests", mp.Tag)
1015

Michael Yang's avatar
Michael Yang committed
1016
1017
	headers := make(http.Header)
	headers.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1018
	resp, err := makeRequestWithRetry(ctx, http.MethodGet, requestURL, headers, nil, regOpts)
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var m *ManifestV2
	if err := json.NewDecoder(resp.Body).Decode(&m); err != nil {
		return nil, err
	}

	return m, err
}

// GetSHA256Digest returns the SHA256 hash of a given buffer and returns it, and the size of buffer
Michael Yang's avatar
Michael Yang committed
1033
func GetSHA256Digest(r io.Reader) (string, int64) {
Michael Yang's avatar
Michael Yang committed
1034
1035
1036
1037
1038
1039
	h := sha256.New()
	n, err := io.Copy(h, r)
	if err != nil {
		log.Fatal(err)
	}

Michael Yang's avatar
Michael Yang committed
1040
	return fmt.Sprintf("sha256:%x", h.Sum(nil)), n
1041
1042
}

1043
1044
var errUnauthorized = fmt.Errorf("unauthorized")

Michael Yang's avatar
Michael Yang committed
1045
func makeRequestWithRetry(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.ReadSeeker, regOpts *RegistryOptions) (*http.Response, error) {
1046
1047
1048
1049
1050
	resp, err := makeRequest(ctx, method, requestURL, headers, body, regOpts)
	if err != nil {
		if !errors.Is(err, context.Canceled) {
			log.Printf("request failed: %v", err)
		}
1051

1052
1053
1054
1055
1056
1057
1058
1059
1060
		return nil, err
	}

	switch {
	case resp.StatusCode == http.StatusUnauthorized:
		// Handle authentication error with one retry
		auth := resp.Header.Get("www-authenticate")
		authRedir := ParseAuthRedirectString(auth)
		token, err := getAuthToken(ctx, authRedir)
Michael Yang's avatar
Michael Yang committed
1061
1062
1063
		if err != nil {
			return nil, err
		}
1064
1065
1066
		regOpts.Token = token
		if body != nil {
			_, err = body.Seek(0, io.SeekStart)
Michael Yang's avatar
Michael Yang committed
1067
1068
1069
1070
			if err != nil {
				return nil, err
			}
		}
1071
1072
1073
1074
1075
1076
1077

		resp, err := makeRequest(ctx, method, requestURL, headers, body, regOpts)
		if resp.StatusCode == http.StatusUnauthorized {
			return nil, errUnauthorized
		}

		return resp, err
1078
1079
1080
1081
1082
1083
1084
1085
	case resp.StatusCode == http.StatusNotFound:
		return nil, os.ErrNotExist
	case resp.StatusCode >= http.StatusBadRequest:
		responseBody, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("%d: %s", resp.StatusCode, err)
		}
		return nil, fmt.Errorf("%d: %s", resp.StatusCode, responseBody)
Michael Yang's avatar
Michael Yang committed
1086
1087
	}

1088
	return resp, nil
Michael Yang's avatar
Michael Yang committed
1089
1090
}

Michael Yang's avatar
Michael Yang committed
1091
func makeRequest(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.Reader, regOpts *RegistryOptions) (*http.Response, error) {
Michael Yang's avatar
Michael Yang committed
1092
	if requestURL.Scheme != "http" && regOpts != nil && regOpts.Insecure {
Michael Yang's avatar
Michael Yang committed
1093
		requestURL.Scheme = "http"
1094
1095
	}

Michael Yang's avatar
Michael Yang committed
1096
	req, err := http.NewRequestWithContext(ctx, method, requestURL.String(), body)
1097
1098
1099
1100
	if err != nil {
		return nil, err
	}

Michael Yang's avatar
Michael Yang committed
1101
1102
1103
1104
	if headers != nil {
		req.Header = headers
	}

Michael Yang's avatar
Michael Yang committed
1105
1106
1107
1108
1109
1110
	if regOpts != nil {
		if regOpts.Token != "" {
			req.Header.Set("Authorization", "Bearer "+regOpts.Token)
		} else if regOpts.Username != "" && regOpts.Password != "" {
			req.SetBasicAuth(regOpts.Username, regOpts.Password)
		}
1111
1112
	}

Michael Yang's avatar
Michael Yang committed
1113
	req.Header.Set("User-Agent", fmt.Sprintf("ollama/%s (%s %s) Go/%s", version.Version, runtime.GOARCH, runtime.GOOS, runtime.Version()))
1114

Michael Yang's avatar
Michael Yang committed
1115
1116
1117
1118
1119
1120
1121
1122
1123
	if s := req.Header.Get("Content-Length"); s != "" {
		contentLength, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			return nil, err
		}

		req.ContentLength = contentLength
	}

Michael Yang's avatar
Michael Yang committed
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
	proxyURL, err := http.ProxyFromEnvironment(req)
	if err != nil {
		return nil, err
	}

	client := http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		},
	}

	resp, err := client.Do(req)
1136
1137
1138
1139
1140
1141
	if err != nil {
		return nil, err
	}

	return resp, nil
}
Michael Yang's avatar
Michael Yang committed
1142

Patrick Devine's avatar
Patrick Devine committed
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
func getValue(header, key string) string {
	startIdx := strings.Index(header, key+"=")
	if startIdx == -1 {
		return ""
	}

	// Move the index to the starting quote after the key.
	startIdx += len(key) + 2
	endIdx := startIdx

	for endIdx < len(header) {
		if header[endIdx] == '"' {
			if endIdx+1 < len(header) && header[endIdx+1] != ',' { // If the next character isn't a comma, continue
				endIdx++
				continue
			}
			break
		}
		endIdx++
	}
	return header[startIdx:endIdx]
}

func ParseAuthRedirectString(authStr string) AuthRedirect {
	authStr = strings.TrimPrefix(authStr, "Bearer ")

	return AuthRedirect{
		Realm:   getValue(authStr, "realm"),
		Service: getValue(authStr, "service"),
		Scope:   getValue(authStr, "scope"),
	}
}

1176
1177
var errDigestMismatch = fmt.Errorf("digest mismatch, file must be downloaded again")

Michael Yang's avatar
Michael Yang committed
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
func verifyBlob(digest string) error {
	fp, err := GetBlobsPath(digest)
	if err != nil {
		return err
	}

	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()

	fileDigest, _ := GetSHA256Digest(f)
	if digest != fileDigest {
1192
		return fmt.Errorf("%w: want %s, got %s", errDigestMismatch, digest, fileDigest)
Michael Yang's avatar
Michael Yang committed
1193
1194
1195
1196
	}

	return nil
}