images.go 26.8 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
func (m *Model) Prompt(p PromptVars) (string, error) {
	var prompt strings.Builder
61
62
	// Use the "missingkey=zero" option to handle missing variables without panicking
	tmpl, err := template.New("").Option("missingkey=zero").Parse(m.Template)
63
64
65
66
	if err != nil {
		return "", err
	}

67
68
69
70
71
	if p.System == "" {
		// use the default system prompt for this model if one is not specified
		p.System = m.System
	}

72
73
74
75
76
	vars := map[string]any{
		"System":   p.System,
		"Prompt":   p.Prompt,
		"Response": p.Response,
		"First":    p.First,
77
78
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
79
	var sb strings.Builder
80
	if err := tmpl.Execute(&sb, vars); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
81
82
83
84
85
86
		return "", err
	}
	prompt.WriteString(sb.String())
	prompt.WriteString(p.Response)
	return prompt.String(), nil
}
87

Bruce MacDonald's avatar
Bruce MacDonald committed
88
89
90
91
92
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
93
94
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
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
121
122
123
124
125
126
127
128
	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)
		}
129
130
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
131
132
133
134
135
136
137
138
	// 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
139
140
}

141
142
143
type ManifestV2 struct {
	SchemaVersion int      `json:"schemaVersion"`
	MediaType     string   `json:"mediaType"`
Michael Yang's avatar
Michael Yang committed
144
	Config        *Layer   `json:"config"`
145
146
147
148
	Layers        []*Layer `json:"layers"`
}

type ConfigV2 struct {
149
	// required by spec
150
151
	Architecture string `json:"architecture"`
	OS           string `json:"os"`
152
153
154
	RootFS       RootFS `json:"rootfs"`

	api.ModelConfiguration
155
156
}

Michael Yang's avatar
Michael Yang committed
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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
	}
}

187
188
189
190
191
type RootFS struct {
	Type    string   `json:"type"`
	DiffIDs []string `json:"diff_ids"`
}

Michael Yang's avatar
Michael Yang committed
192
func (m *ManifestV2) GetTotalSize() (total int64) {
Patrick Devine's avatar
Patrick Devine committed
193
194
195
	for _, layer := range m.Layers {
		total += layer.Size
	}
Michael Yang's avatar
Michael Yang committed
196

Patrick Devine's avatar
Patrick Devine committed
197
198
199
200
	total += m.Config.Size
	return total
}

Patrick Devine's avatar
Patrick Devine committed
201
func GetManifest(mp ModelPath) (*ManifestV2, string, error) {
202
	fp, err := mp.GetManifestPath()
203
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
204
		return nil, "", err
205
	}
206

207
	if _, err = os.Stat(fp); err != nil {
Patrick Devine's avatar
Patrick Devine committed
208
		return nil, "", err
209
210
211
212
	}

	var manifest *ManifestV2

213
	bts, err := os.ReadFile(fp)
214
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
215
		return nil, "", fmt.Errorf("couldn't open file '%s'", fp)
216
217
	}

Patrick Devine's avatar
Patrick Devine committed
218
219
220
	shaSum := sha256.Sum256(bts)
	shaStr := hex.EncodeToString(shaSum[:])

221
	if err := json.Unmarshal(bts, &manifest); err != nil {
Patrick Devine's avatar
Patrick Devine committed
222
		return nil, "", err
223
224
	}

Patrick Devine's avatar
Patrick Devine committed
225
	return manifest, shaStr, nil
226
227
228
}

func GetModel(name string) (*Model, error) {
229
	mp := ParseModelPath(name)
Patrick Devine's avatar
Patrick Devine committed
230
	manifest, digest, err := GetManifest(mp)
231
232
233
234
235
	if err != nil {
		return nil, err
	}

	model := &Model{
236
237
238
239
240
		Name:      mp.GetFullTagname(),
		ShortName: mp.GetShortTagname(),
		Digest:    digest,
		Template:  "{{ .Prompt }}",
		License:   []string{},
241
242
	}

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
	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
	}

258
	for _, layer := range manifest.Layers {
Patrick Devine's avatar
Patrick Devine committed
259
		filename, err := GetBlobsPath(layer.Digest)
260
261
262
263
		if err != nil {
			return nil, err
		}

264
265
266
		switch layer.MediaType {
		case "application/vnd.ollama.image.model":
			model.ModelPath = filename
Patrick Devine's avatar
Patrick Devine committed
267
			model.OriginalModel = layer.From
268
		case "application/vnd.ollama.image.embed":
269
270
271
			// 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.")
272
273
		case "application/vnd.ollama.image.adapter":
			model.AdapterPaths = append(model.AdapterPaths, filename)
Michael Yang's avatar
Michael Yang committed
274
275
		case "application/vnd.ollama.image.projector":
			model.ProjectorPaths = append(model.ProjectorPaths, filename)
276
277
278
279
280
281
282
283
284
		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)
285
286
287
			if err != nil {
				return nil, err
			}
288
289

			model.System = string(bts)
290
291
292
293
294
295
296
		case "application/vnd.ollama.image.prompt":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}

			model.Template = string(bts)
297
		case "application/vnd.ollama.image.params":
Michael Yang's avatar
Michael Yang committed
298
299
300
301
302
			params, err := os.Open(filename)
			if err != nil {
				return nil, err
			}
			defer params.Close()
303

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

	return model, nil
}

320
321
func realpath(mfDir, from string) string {
	abspath, err := filepath.Abs(from)
Michael Yang's avatar
Michael Yang committed
322
	if err != nil {
323
		return from
324
325
	}

Michael Yang's avatar
Michael Yang committed
326
	home, err := os.UserHomeDir()
327
	if err != nil {
Michael Yang's avatar
Michael Yang committed
328
		return abspath
329
330
	}

331
	if from == "~" {
Michael Yang's avatar
Michael Yang committed
332
		return home
333
334
335
336
337
338
339
	} 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)
340
341
	}

Michael Yang's avatar
Michael Yang committed
342
343
344
	return abspath
}

345
func CreateModel(ctx context.Context, name, modelFileDir string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
346
347
	config := ConfigV2{
		OS:           "linux",
Michael Yang's avatar
Michael Yang committed
348
		Architecture: "amd64",
Michael Yang's avatar
Michael Yang committed
349
350
351
		RootFS: RootFS{
			Type: "layers",
		},
352
353
	}

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

Michael Yang's avatar
Michael Yang committed
356
	var layers Layers
Michael Yang's avatar
Michael Yang committed
357

358
	params := make(map[string][]string)
Michael Yang's avatar
Michael Yang committed
359
360
	fromParams := make(map[string]any)

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

365
366
		switch c.Name {
		case "model":
Michael Yang's avatar
Michael Yang committed
367
368
369
370
371
372
373
374
375
			if strings.HasPrefix(c.Args, "@") {
				blobPath, err := GetBlobsPath(strings.TrimPrefix(c.Args, "@"))
				if err != nil {
					return err
				}

				c.Args = blobPath
			}

376
			bin, err := os.Open(realpath(modelFileDir, c.Args))
377
			if err != nil {
Michael Yang's avatar
Michael Yang committed
378
379
380
381
382
383
384
				// 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 {
385
386
387
						return err
					}

Michael Yang's avatar
Michael Yang committed
388
					manifest, _, err = GetManifest(modelpath)
389
390
391
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
392
393
				case err != nil:
					return err
394
				}
395

396
				fn(api.ProgressResponse{Status: "reading model metadata"})
Michael Yang's avatar
Michael Yang committed
397
				fromConfigPath, err := GetBlobsPath(manifest.Config.Digest)
Michael Yang's avatar
Michael Yang committed
398
399
400
401
				if err != nil {
					return err
				}

Michael Yang's avatar
Michael Yang committed
402
				fromConfigFile, err := os.Open(fromConfigPath)
Michael Yang's avatar
Michael Yang committed
403
404
405
				if err != nil {
					return err
				}
Michael Yang's avatar
Michael Yang committed
406
				defer fromConfigFile.Close()
Michael Yang's avatar
Michael Yang committed
407

Michael Yang's avatar
Michael Yang committed
408
409
				var fromConfig ConfigV2
				if err := json.NewDecoder(fromConfigFile).Decode(&fromConfig); err != nil {
Michael Yang's avatar
Michael Yang committed
410
411
412
					return err
				}

Michael Yang's avatar
Michael Yang committed
413
414
415
416
				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
417

Michael Yang's avatar
Michael Yang committed
418
419
420
421
				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
422
423
424
425
						if err != nil {
							return err
						}

Michael Yang's avatar
Michael Yang committed
426
						fromParamsFile, err := os.Open(fromParamsPath)
Michael Yang's avatar
Michael Yang committed
427
428
429
						if err != nil {
							return err
						}
Michael Yang's avatar
Michael Yang committed
430
						defer fromParamsFile.Close()
Michael Yang's avatar
Michael Yang committed
431

Michael Yang's avatar
Michael Yang committed
432
						if err := json.NewDecoder(fromParamsFile).Decode(&fromParams); err != nil {
Michael Yang's avatar
Michael Yang committed
433
434
435
436
							return err
						}
					}

Michael Yang's avatar
Michael Yang committed
437
					layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, modelpath.GetShortTagname())
438
439
440
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
441

Michael Yang's avatar
Michael Yang committed
442
					layers.Add(layer)
443
				}
Michael Yang's avatar
Michael Yang committed
444
445
446

				deleteMap[manifest.Config.Digest] = struct{}{}
				continue
447
			}
Michael Yang's avatar
Michael Yang committed
448
			defer bin.Close()
449

450
451
452
			var offset int64
			for {
				fn(api.ProgressResponse{Status: "creating model layer"})
453

454
455
456
457
458
459
460
				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
461

Michael Yang's avatar
Michael Yang committed
462
463
464
465
				config.SetModelFormat(ggml.Name())
				config.SetModelFamily(ggml.ModelFamily())
				config.SetModelType(ggml.ModelType())
				config.SetFileType(ggml.FileType())
466

467
468
469
470
				mediatype := mediatype
				if ggml.ModelFamily() == "clip" {
					mediatype = "application/vnd.ollama.image.projector"
				}
471

472
473
474
475
476
477
478
479
480
481
				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
482
		case "adapter":
483
484
485
486
487
488
489
490
			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
491

Michael Yang's avatar
Michael Yang committed
492
			fn(api.ProgressResponse{Status: "creating adapter layer"})
493
			bin, err := os.Open(realpath(modelFileDir, c.Args))
494
			if err != nil {
Michael Yang's avatar
Michael Yang committed
495
				return err
496
			}
Michael Yang's avatar
Michael Yang committed
497
			defer bin.Close()
498

Michael Yang's avatar
Michael Yang committed
499
			layer, err := NewLayer(bin, mediatype)
500
			if err != nil {
Michael Yang's avatar
Michael Yang committed
501
				return err
502
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
503

Michael Yang's avatar
Michael Yang committed
504
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
505
506
		case "license":
			fn(api.ProgressResponse{Status: "creating license layer"})
Michael Yang's avatar
Michael Yang committed
507
508
509

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

Michael Yang's avatar
Michael Yang committed
514
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
515
516
517
		case "template", "system":
			fn(api.ProgressResponse{Status: fmt.Sprintf("creating %s layer", c.Name)})

Michael Yang's avatar
Michael Yang committed
518
519
			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
520
			if err != nil {
521
				return err
522
			}
523

Michael Yang's avatar
Michael Yang committed
524
			layers.Replace(layer)
525
		default:
526
			params[c.Name] = append(params[c.Name], c.Args)
527
528
529
		}
	}

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

533
		formattedParams, err := api.FormatParams(params)
534
		if err != nil {
Michael Yang's avatar
Michael Yang committed
535
			return err
536
		}
537

Michael Yang's avatar
Michael Yang committed
538
		for k, v := range fromParams {
Michael Yang's avatar
Michael Yang committed
539
540
541
542
543
			if _, ok := formattedParams[k]; !ok {
				formattedParams[k] = v
			}
		}

Michael Yang's avatar
Michael Yang committed
544
		if config.ModelType == "65B" {
Michael Yang's avatar
Michael Yang committed
545
			if gqa, ok := formattedParams["gqa"].(int); ok && gqa == 8 {
Michael Yang's avatar
Michael Yang committed
546
547
548
549
				config.ModelType = "70B"
			}
		}

Michael Yang's avatar
Michael Yang committed
550
551
		var b bytes.Buffer
		if err := json.NewEncoder(&b).Encode(formattedParams); err != nil {
552
553
554
			return err
		}

Michael Yang's avatar
Michael Yang committed
555
		fn(api.ProgressResponse{Status: "creating config layer"})
Michael Yang's avatar
Michael Yang committed
556
		layer, err := NewLayer(&b, "application/vnd.ollama.image.params")
557
		if err != nil {
Michael Yang's avatar
Michael Yang committed
558
			return err
559
		}
Michael Yang's avatar
Michael Yang committed
560

Michael Yang's avatar
Michael Yang committed
561
		layers.Replace(layer)
562
563
	}

Michael Yang's avatar
Michael Yang committed
564
565
566
	digests := make([]string, len(layers.items))
	for i, layer := range layers.items {
		digests[i] = layer.Digest
567
568
	}

Michael Yang's avatar
Michael Yang committed
569
	config.RootFS.DiffIDs = digests
Michael Yang's avatar
Michael Yang committed
570

Michael Yang's avatar
Michael Yang committed
571
572
	var b bytes.Buffer
	if err := json.NewEncoder(&b).Encode(config); err != nil {
573
574
575
		return err
	}

Michael Yang's avatar
Michael Yang committed
576
577
	configLayer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json")
	if err != nil {
578
579
580
		return err
	}

Michael Yang's avatar
Michael Yang committed
581
	delete(deleteMap, configLayer.Digest)
582

Michael Yang's avatar
Michael Yang committed
583
584
	for _, layer := range append(layers.items, configLayer) {
		committed, err := layer.Commit()
585
586
587
		if err != nil {
			return err
		}
588

Michael Yang's avatar
Michael Yang committed
589
590
591
		status := "writing layer"
		if !committed {
			status = "using already created layer"
592
593
		}

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

Michael Yang's avatar
Michael Yang committed
596
		delete(deleteMap, layer.Digest)
597
598
	}

Michael Yang's avatar
Michael Yang committed
599
600
	fn(api.ProgressResponse{Status: "writing manifest"})
	if err := WriteManifest(name, configLayer, layers.items); err != nil {
601
602
		return err
	}
603

Michael Yang's avatar
Michael Yang committed
604
605
606
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := deleteUnusedLayers(nil, deleteMap, false); err != nil {
			return err
607
608
609
		}
	}

Michael Yang's avatar
Michael Yang committed
610
611
	fn(api.ProgressResponse{Status: "success"})
	return nil
612
613
}

Patrick Devine's avatar
Patrick Devine committed
614
func CopyModel(src, dest string) error {
615
	srcModelPath := ParseModelPath(src)
616
	srcPath, err := srcModelPath.GetManifestPath()
617
618
619
620
	if err != nil {
		return err
	}

621
	destModelPath := ParseModelPath(dest)
622
	destPath, err := destModelPath.GetManifestPath()
Patrick Devine's avatar
Patrick Devine committed
623
624
625
	if err != nil {
		return err
	}
626
627
628
	if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
629
630

	// copy the file
Michael Yang's avatar
Michael Yang committed
631
	input, err := os.ReadFile(srcPath)
Patrick Devine's avatar
Patrick Devine committed
632
633
634
635
636
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

Michael Yang's avatar
Michael Yang committed
637
	err = os.WriteFile(destPath, input, 0o644)
Patrick Devine's avatar
Patrick Devine committed
638
639
640
641
642
643
644
645
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

	return nil
}

Michael Yang's avatar
Michael Yang committed
646
func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}, dryRun bool) error {
647
648
649
650
	fp, err := GetManifestPath()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
651
652
653
654

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

Michael Yang's avatar
Michael Yang committed
657
658
659
660
		dir, file := filepath.Split(path)
		dir = strings.Trim(strings.TrimPrefix(dir, fp), string(os.PathSeparator))
		tag := strings.Join([]string{dir, file}, ":")
		fmp := ParseModelPath(tag)
661

Michael Yang's avatar
Michael Yang committed
662
		// skip the manifest we're trying to delete
663
		if skipModelPath != nil && skipModelPath.GetFullTagname() == fmp.GetFullTagname() {
Michael Yang's avatar
Michael Yang committed
664
			return nil
665
		}
Michael Yang's avatar
Michael Yang committed
666
667
668
669
670
671
672
673
674
675
676
677

		// 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)
678
		return nil
Michael Yang's avatar
Michael Yang committed
679
680
681
	}

	if err := filepath.Walk(fp, walkFunc); err != nil {
Michael Yang's avatar
Michael Yang committed
682
683
		return err
	}
684
685

	// only delete the files which are still in the deleteMap
Michael Yang's avatar
Michael Yang committed
686
687
688
689
690
691
692
693
694
	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)
695
696
				continue
			}
Michael Yang's avatar
Michael Yang committed
697
698
		} else {
			log.Printf("wanted to remove: %s", fp)
699
700
701
		}
	}

702
703
704
705
	return nil
}

func PruneLayers() error {
Michael Yang's avatar
Michael Yang committed
706
	deleteMap := make(map[string]struct{})
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
	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
723
724
725
		if strings.HasPrefix(name, "sha256:") {
			deleteMap[name] = struct{}{}
		}
726
727
728
729
730
731
732
733
734
735
736
737
738
739
	}

	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
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
765
766
767
768
769
770
771
772
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
}

773
774
775
776
777
778
779
func DeleteModel(name string) error {
	mp := ParseModelPath(name)
	manifest, _, err := GetManifest(mp)
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
780
	deleteMap := make(map[string]struct{})
781
	for _, layer := range manifest.Layers {
Michael Yang's avatar
Michael Yang committed
782
		deleteMap[layer.Digest] = struct{}{}
783
	}
Michael Yang's avatar
Michael Yang committed
784
	deleteMap[manifest.Config.Digest] = struct{}{}
785
786
787
788
789
790

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

791
	fp, err := mp.GetManifestPath()
792
793
794
795
796
797
798
799
800
801
802
803
	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
804
func ShowModelfile(model *Model) (string, error) {
Michael Yang's avatar
Michael Yang committed
805
	var mt struct {
Patrick Devine's avatar
Patrick Devine committed
806
		*Model
Michael Yang's avatar
Michael Yang committed
807
		From       string
Michael Yang's avatar
Michael Yang committed
808
		Parameters map[string][]any
Patrick Devine's avatar
Patrick Devine committed
809
810
	}

Michael Yang's avatar
Michael Yang committed
811
	mt.Parameters = make(map[string][]any)
Patrick Devine's avatar
Patrick Devine committed
812
	for k, v := range model.Options {
Michael Yang's avatar
Michael Yang committed
813
814
815
		if s, ok := v.([]any); ok {
			mt.Parameters[k] = s
			continue
Patrick Devine's avatar
Patrick Devine committed
816
817
		}

Michael Yang's avatar
Michael Yang committed
818
		mt.Parameters[k] = []any{v}
Patrick Devine's avatar
Patrick Devine committed
819
820
	}

Michael Yang's avatar
Michael Yang committed
821
822
	mt.Model = model
	mt.From = model.ModelPath
Patrick Devine's avatar
Patrick Devine committed
823

Michael Yang's avatar
Michael Yang committed
824
	if model.OriginalModel != "" {
Daniel Reis's avatar
Daniel Reis committed
825
		mt.From = model.OriginalModel
Patrick Devine's avatar
Patrick Devine committed
826
827
828
829
830
831
832
833
	}

	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 }}"""
834
835

{{- if .System }}
Patrick Devine's avatar
Patrick Devine committed
836
SYSTEM """{{ .System }}"""
837
{{- end }}
838
839
840
841

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

Michael Yang's avatar
Michael Yang committed
843
844
845
846
{{- range $k, $v := .Parameters }}
{{- range $parameter := $v }}
PARAMETER {{ $k }} {{ printf "%#v" $parameter }}
{{- end }}
Michael Yang's avatar
Michael Yang committed
847
{{- end }}`
Patrick Devine's avatar
Patrick Devine committed
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864

	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
}

865
func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
866
	mp := ParseModelPath(name)
867
868
	fn(api.ProgressResponse{Status: "retrieving manifest"})

869
870
871
872
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
873
	manifest, _, err := GetManifest(mp)
874
	if err != nil {
875
		fn(api.ProgressResponse{Status: "couldn't retrieve manifest"})
876
877
878
879
		return err
	}

	var layers []*Layer
Jeffrey Morgan's avatar
Jeffrey Morgan committed
880
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
881
	layers = append(layers, manifest.Config)
882
883

	for _, layer := range layers {
Michael Yang's avatar
Michael Yang committed
884
		if err := uploadBlob(ctx, mp, layer, regOpts, fn); err != nil {
885
			log.Printf("error uploading blob: %v", err)
886
887
888
			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())
			}
889
890
			return err
		}
891
892
	}

893
	fn(api.ProgressResponse{Status: "pushing manifest"})
Michael Yang's avatar
Michael Yang committed
894
895
	requestURL := mp.BaseURL()
	requestURL = requestURL.JoinPath("v2", mp.GetNamespaceRepository(), "manifests", mp.Tag)
896
897
898
899
900
901

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

Michael Yang's avatar
Michael Yang committed
902
903
	headers := make(http.Header)
	headers.Set("Content-Type", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
904
	resp, err := makeRequestWithRetry(ctx, http.MethodPut, requestURL, headers, bytes.NewReader(manifestJSON), regOpts)
905
906
907
908
909
	if err != nil {
		return err
	}
	defer resp.Body.Close()

910
	fn(api.ProgressResponse{Status: "success"})
911
912
913
914

	return nil
}

915
func PullModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
916
917
	mp := ParseModelPath(name)

918
919
920
921
922
	var manifest *ManifestV2
	var err error
	var noprune string

	// build deleteMap to prune unused layers
Michael Yang's avatar
Michael Yang committed
923
	deleteMap := make(map[string]struct{})
924
925
926
927
928
929
930
931
932

	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
933
				deleteMap[l.Digest] = struct{}{}
934
			}
Michael Yang's avatar
Michael Yang committed
935
			deleteMap[manifest.Config.Digest] = struct{}{}
936
937
938
		}
	}

939
940
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
941
	}
942

943
	fn(api.ProgressResponse{Status: "pulling manifest"})
944

945
	manifest, err = pullModelManifest(ctx, mp, regOpts)
946
	if err != nil {
947
		return fmt.Errorf("pull model manifest: %s", err)
948
949
950
	}

	var layers []*Layer
Bruce MacDonald's avatar
Bruce MacDonald committed
951
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
952
	layers = append(layers, manifest.Config)
953
954

	for _, layer := range layers {
955
956
957
958
959
960
961
962
		if err := downloadBlob(
			ctx,
			downloadOpts{
				mp:      mp,
				digest:  layer.Digest,
				regOpts: regOpts,
				fn:      fn,
			}); err != nil {
963
964
			return err
		}
965
		delete(deleteMap, layer.Digest)
966
	}
967
	delete(deleteMap, manifest.Config.Digest)
968

Michael Yang's avatar
Michael Yang committed
969
970
971
	fn(api.ProgressResponse{Status: "verifying sha256 digest"})
	for _, layer := range layers {
		if err := verifyBlob(layer.Digest); err != nil {
972
973
974
975
976
977
978
979
980
981
982
			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
983
984
985
986
			return err
		}
	}

987
	fn(api.ProgressResponse{Status: "writing manifest"})
988

989
	manifestJSON, err := json.Marshal(manifest)
990
991
992
993
	if err != nil {
		return err
	}

994
	fp, err := mp.GetManifestPath()
995
996
997
	if err != nil {
		return err
	}
998
999
1000
	if err := os.MkdirAll(filepath.Dir(fp), 0o755); err != nil {
		return err
	}
1001

Bruce MacDonald's avatar
Bruce MacDonald committed
1002
	err = os.WriteFile(fp, manifestJSON, 0o644)
1003
1004
1005
1006
1007
	if err != nil {
		log.Printf("couldn't write to %s", fp)
		return err
	}

1008
1009
1010
1011
1012
1013
1014
1015
	if noprune == "" {
		fn(api.ProgressResponse{Status: "removing any unused layers"})
		err = deleteUnusedLayers(nil, deleteMap, false)
		if err != nil {
			return err
		}
	}

1016
	fn(api.ProgressResponse{Status: "success"})
1017
1018
1019
1020

	return nil
}

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

Michael Yang's avatar
Michael Yang committed
1024
1025
	headers := make(http.Header)
	headers.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1026
	resp, err := makeRequestWithRetry(ctx, http.MethodGet, requestURL, headers, nil, regOpts)
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
	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
1041
func GetSHA256Digest(r io.Reader) (string, int64) {
Michael Yang's avatar
Michael Yang committed
1042
1043
1044
1045
1046
1047
	h := sha256.New()
	n, err := io.Copy(h, r)
	if err != nil {
		log.Fatal(err)
	}

Michael Yang's avatar
Michael Yang committed
1048
	return fmt.Sprintf("sha256:%x", h.Sum(nil)), n
1049
1050
}

1051
1052
var errUnauthorized = fmt.Errorf("unauthorized")

Michael Yang's avatar
Michael Yang committed
1053
func makeRequestWithRetry(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.ReadSeeker, regOpts *RegistryOptions) (*http.Response, error) {
1054
1055
1056
1057
1058
	resp, err := makeRequest(ctx, method, requestURL, headers, body, regOpts)
	if err != nil {
		if !errors.Is(err, context.Canceled) {
			log.Printf("request failed: %v", err)
		}
1059

1060
1061
1062
1063
1064
1065
1066
1067
1068
		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
1069
1070
1071
		if err != nil {
			return nil, err
		}
1072
1073
1074
		regOpts.Token = token
		if body != nil {
			_, err = body.Seek(0, io.SeekStart)
Michael Yang's avatar
Michael Yang committed
1075
1076
1077
1078
			if err != nil {
				return nil, err
			}
		}
1079
1080
1081
1082
1083
1084
1085

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

		return resp, err
1086
1087
1088
1089
1090
1091
1092
1093
	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
1094
1095
	}

1096
	return resp, nil
Michael Yang's avatar
Michael Yang committed
1097
1098
}

Michael Yang's avatar
Michael Yang committed
1099
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
1100
	if requestURL.Scheme != "http" && regOpts != nil && regOpts.Insecure {
Michael Yang's avatar
Michael Yang committed
1101
		requestURL.Scheme = "http"
1102
1103
	}

Michael Yang's avatar
Michael Yang committed
1104
	req, err := http.NewRequestWithContext(ctx, method, requestURL.String(), body)
1105
1106
1107
1108
	if err != nil {
		return nil, err
	}

Michael Yang's avatar
Michael Yang committed
1109
1110
1111
1112
	if headers != nil {
		req.Header = headers
	}

Michael Yang's avatar
Michael Yang committed
1113
1114
1115
1116
1117
1118
	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)
		}
1119
1120
	}

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

Michael Yang's avatar
Michael Yang committed
1123
1124
1125
1126
1127
1128
1129
1130
1131
	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
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
	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)
1144
1145
1146
1147
1148
1149
	if err != nil {
		return nil, err
	}

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

Patrick Devine's avatar
Patrick Devine committed
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
1176
1177
1178
1179
1180
1181
1182
1183
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"),
	}
}

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

Michael Yang's avatar
Michael Yang committed
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
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 {
1200
		return fmt.Errorf("%w: want %s, got %s", errDigestMismatch, digest, fileDigest)
Michael Yang's avatar
Michael Yang committed
1201
1202
1203
1204
	}

	return nil
}