images.go 30.4 KB
Newer Older
1
2
3
package server

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

Michael Yang's avatar
Michael Yang committed
26
27
	"golang.org/x/exp/slices"

28
	"github.com/ollama/ollama/api"
29
	"github.com/ollama/ollama/auth"
30
	"github.com/ollama/ollama/convert"
Michael Yang's avatar
Michael Yang committed
31
	"github.com/ollama/ollama/format"
32
33
	"github.com/ollama/ollama/llm"
	"github.com/ollama/ollama/parser"
34
	"github.com/ollama/ollama/types/errtypes"
Michael Yang's avatar
Michael Yang committed
35
	"github.com/ollama/ollama/types/model"
36
	"github.com/ollama/ollama/version"
37
38
)

Michael Yang's avatar
Michael Yang committed
39
40
41
42
43
44
45
type registryOptions struct {
	Insecure bool
	Username string
	Password string
	Token    string
}

46
type Model struct {
Michael Yang's avatar
Michael Yang committed
47
	Name           string `json:"name"`
48
	Config         ConfigV2
Michael Yang's avatar
Michael Yang committed
49
50
	ShortName      string
	ModelPath      string
51
	ParentModel    string
Michael Yang's avatar
Michael Yang committed
52
53
54
55
56
57
	AdapterPaths   []string
	ProjectorPaths []string
	Template       string
	System         string
	License        []string
	Digest         string
Patrick Devine's avatar
Patrick Devine committed
58
	Size           int64
Michael Yang's avatar
Michael Yang committed
59
	Options        map[string]interface{}
60
61
62
	Messages       []Message
}

63
64
65
66
func (m *Model) IsEmbedding() bool {
	return slices.Contains(m.Config.ModelFamilies, "bert") || slices.Contains(m.Config.ModelFamilies, "nomic-bert")
}

67
68
69
type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
70
71
72
73
74
}

type ManifestV2 struct {
	SchemaVersion int      `json:"schemaVersion"`
	MediaType     string   `json:"mediaType"`
Michael Yang's avatar
Michael Yang committed
75
	Config        *Layer   `json:"config"`
76
77
78
79
	Layers        []*Layer `json:"layers"`
}

type ConfigV2 struct {
80
81
82
83
84
85
	ModelFormat   string   `json:"model_format"`
	ModelFamily   string   `json:"model_family"`
	ModelFamilies []string `json:"model_families"`
	ModelType     string   `json:"model_type"`
	FileType      string   `json:"file_type"`

86
	// required by spec
87
88
	Architecture string `json:"architecture"`
	OS           string `json:"os"`
89
	RootFS       RootFS `json:"rootfs"`
90
91
}

Michael Yang's avatar
Michael Yang committed
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
121
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
	}
}

122
123
124
125
126
type RootFS struct {
	Type    string   `json:"type"`
	DiffIDs []string `json:"diff_ids"`
}

Michael Yang's avatar
Michael Yang committed
127
func (m *ManifestV2) GetTotalSize() (total int64) {
Patrick Devine's avatar
Patrick Devine committed
128
129
130
	for _, layer := range m.Layers {
		total += layer.Size
	}
Michael Yang's avatar
Michael Yang committed
131

Patrick Devine's avatar
Patrick Devine committed
132
133
134
135
	total += m.Config.Size
	return total
}

Patrick Devine's avatar
Patrick Devine committed
136
func GetManifest(mp ModelPath) (*ManifestV2, string, error) {
137
	fp, err := mp.GetManifestPath()
138
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
139
		return nil, "", err
140
	}
141

142
	if _, err = os.Stat(fp); err != nil {
Patrick Devine's avatar
Patrick Devine committed
143
		return nil, "", err
144
145
146
147
	}

	var manifest *ManifestV2

148
	bts, err := os.ReadFile(fp)
149
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
150
		return nil, "", fmt.Errorf("couldn't open file '%s'", fp)
151
152
	}

Patrick Devine's avatar
Patrick Devine committed
153
154
155
	shaSum := sha256.Sum256(bts)
	shaStr := hex.EncodeToString(shaSum[:])

156
	if err := json.Unmarshal(bts, &manifest); err != nil {
Patrick Devine's avatar
Patrick Devine committed
157
		return nil, "", err
158
159
	}

Patrick Devine's avatar
Patrick Devine committed
160
	return manifest, shaStr, nil
161
162
163
}

func GetModel(name string) (*Model, error) {
164
	mp := ParseModelPath(name)
Patrick Devine's avatar
Patrick Devine committed
165
	manifest, digest, err := GetManifest(mp)
166
167
168
169
170
	if err != nil {
		return nil, err
	}

	model := &Model{
171
172
173
174
175
		Name:      mp.GetFullTagname(),
		ShortName: mp.GetShortTagname(),
		Digest:    digest,
		Template:  "{{ .Prompt }}",
		License:   []string{},
Patrick Devine's avatar
Patrick Devine committed
176
		Size:      manifest.GetTotalSize(),
177
178
	}

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
	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
	}

194
	for _, layer := range manifest.Layers {
Patrick Devine's avatar
Patrick Devine committed
195
		filename, err := GetBlobsPath(layer.Digest)
196
197
198
199
		if err != nil {
			return nil, err
		}

200
201
202
		switch layer.MediaType {
		case "application/vnd.ollama.image.model":
			model.ModelPath = filename
203
			model.ParentModel = layer.From
204
		case "application/vnd.ollama.image.embed":
205
206
			// Deprecated in versions  > 0.1.2
			// TODO: remove this warning in a future version
207
			slog.Info("WARNING: model contains embeddings, but embeddings in modelfiles have been deprecated and will be ignored.")
208
209
		case "application/vnd.ollama.image.adapter":
			model.AdapterPaths = append(model.AdapterPaths, filename)
Michael Yang's avatar
Michael Yang committed
210
211
		case "application/vnd.ollama.image.projector":
			model.ProjectorPaths = append(model.ProjectorPaths, filename)
212
213
214
215
216
217
218
219
220
		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)
221
222
223
			if err != nil {
				return nil, err
			}
224
225

			model.System = string(bts)
226
227
228
229
230
231
232
		case "application/vnd.ollama.image.prompt":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}

			model.Template = string(bts)
233
		case "application/vnd.ollama.image.params":
Michael Yang's avatar
Michael Yang committed
234
235
236
237
238
			params, err := os.Open(filename)
			if err != nil {
				return nil, err
			}
			defer params.Close()
239

240
			// parse model options parameters into a map so that we can see which fields have been specified explicitly
241
			if err = json.NewDecoder(params).Decode(&model.Options); err != nil {
242
243
				return nil, err
			}
244
245
246
247
248
249
250
251
252
253
		case "application/vnd.ollama.image.messages":
			msgs, err := os.Open(filename)
			if err != nil {
				return nil, err
			}
			defer msgs.Close()

			if err = json.NewDecoder(msgs).Decode(&model.Messages); err != nil {
				return nil, err
			}
Patrick Devine's avatar
Patrick Devine committed
254
255
256
257
258
259
		case "application/vnd.ollama.image.license":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}
			model.License = append(model.License, string(bts))
260
261
262
263
264
265
		}
	}

	return model, nil
}

266
267
func realpath(mfDir, from string) string {
	abspath, err := filepath.Abs(from)
Michael Yang's avatar
Michael Yang committed
268
	if err != nil {
269
		return from
270
271
	}

Michael Yang's avatar
Michael Yang committed
272
	home, err := os.UserHomeDir()
273
	if err != nil {
Michael Yang's avatar
Michael Yang committed
274
		return abspath
275
276
	}

277
	if from == "~" {
Michael Yang's avatar
Michael Yang committed
278
		return home
279
280
281
282
283
284
285
	} 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)
286
287
	}

Michael Yang's avatar
Michael Yang committed
288
289
290
	return abspath
}

Michael Yang's avatar
Michael Yang committed
291
func CreateModel(ctx context.Context, name, modelFileDir, quantization string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
292
293
294
295
296
297
298
	deleteMap := make(map[string]struct{})
	if manifest, _, err := GetManifest(ParseModelPath(name)); err == nil {
		for _, layer := range append(manifest.Layers, manifest.Config) {
			deleteMap[layer.Digest] = struct{}{}
		}
	}

299
300
	config := ConfigV2{
		OS:           "linux",
Michael Yang's avatar
Michael Yang committed
301
		Architecture: "amd64",
Michael Yang's avatar
Michael Yang committed
302
303
304
		RootFS: RootFS{
			Type: "layers",
		},
305
306
	}

Michael Yang's avatar
Michael Yang committed
307
	var layers Layers
308
	messages := []string{}
Michael Yang's avatar
Michael Yang committed
309

310
	params := make(map[string][]string)
Michael Yang's avatar
Michael Yang committed
311
312
	fromParams := make(map[string]any)

313
	for _, c := range commands {
Michael Yang's avatar
Michael Yang committed
314
315
		mediatype := fmt.Sprintf("application/vnd.ollama.image.%s", c.Name)

316
317
		switch c.Name {
		case "model":
Michael Yang's avatar
Michael Yang committed
318
319
320
321
322
323
324
325
326
			if strings.HasPrefix(c.Args, "@") {
				blobPath, err := GetBlobsPath(strings.TrimPrefix(c.Args, "@"))
				if err != nil {
					return err
				}

				c.Args = blobPath
			}

327
328
			pathName := realpath(modelFileDir, c.Args)

329
			ggufName, err := convertModel(name, pathName, fn)
330
			if err != nil {
331
				var pathErr *fs.PathError
332
333
334
				switch {
				case errors.Is(err, zip.ErrFormat):
					// it's not a safetensor archive
335
336
				case errors.As(err, &pathErr):
					// it's not a file on disk, could be a model reference
337
338
339
340
341
342
343
344
				default:
					return err
				}
			}

			if ggufName != "" {
				pathName = ggufName
				defer os.RemoveAll(ggufName)
Michael Yang's avatar
Michael Yang committed
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

				if quantization != "" {
					quantization = strings.ToUpper(quantization)
					fn(api.ProgressResponse{Status: fmt.Sprintf("quantizing %s model to %s", "F16", quantization)})
					tempfile, err := os.CreateTemp(filepath.Dir(ggufName), quantization)
					if err != nil {
						return err
					}
					defer os.RemoveAll(tempfile.Name())

					if err := llm.Quantize(ggufName, tempfile.Name(), quantization); err != nil {
						return err
					}

					if err := tempfile.Close(); err != nil {
						return err
					}

					pathName = tempfile.Name()
				}
365
366
367
			}

			bin, err := os.Open(pathName)
368
			if err != nil {
Michael Yang's avatar
Michael Yang committed
369
370
371
372
373
374
				// 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"})
Michael Yang's avatar
Michael Yang committed
375
					if err := PullModel(ctx, c.Args, &registryOptions{}, fn); err != nil {
376
377
378
						return err
					}

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

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

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

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

Bruce MacDonald's avatar
Bruce MacDonald committed
404
405
406
407
408
				// if the model is still not in gguf format, error out
				if fromConfig.ModelFormat != "gguf" {
					return fmt.Errorf("%s is not in gguf format, this base model is not compatible with this version of ollama", c.Args)
				}

Michael Yang's avatar
Michael Yang committed
409
410
411
412
				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
413

Michael Yang's avatar
Michael Yang committed
414
415
416
417
				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
418
419
420
421
						if err != nil {
							return err
						}

Michael Yang's avatar
Michael Yang committed
422
						fromParamsFile, err := os.Open(fromParamsPath)
Michael Yang's avatar
Michael Yang committed
423
424
425
						if err != nil {
							return err
						}
Michael Yang's avatar
Michael Yang committed
426
						defer fromParamsFile.Close()
Michael Yang's avatar
Michael Yang committed
427

Michael Yang's avatar
Michael Yang committed
428
						if err := json.NewDecoder(fromParamsFile).Decode(&fromParams); err != nil {
Michael Yang's avatar
Michael Yang committed
429
430
431
432
							return err
						}
					}

Michael Yang's avatar
Michael Yang committed
433
					layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, modelpath.GetShortTagname())
434
435
436
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
437

Michael Yang's avatar
Michael Yang committed
438
					layers.Add(layer)
439
				}
Michael Yang's avatar
Michael Yang committed
440
441
442

				deleteMap[manifest.Config.Digest] = struct{}{}
				continue
443
			}
Michael Yang's avatar
Michael Yang committed
444
			defer bin.Close()
445

446
447
448
			var offset int64
			for {
				fn(api.ProgressResponse{Status: "creating model layer"})
449
450
451
				if _, err := bin.Seek(offset, io.SeekStart); err != nil {
					return err
				}
452

Michael Yang's avatar
Michael Yang committed
453
454
455
456
457
458
459
				ggml, size, err := llm.DecodeGGML(bin)
				if errors.Is(err, io.EOF) {
					break
				} else if errors.Is(err, llm.ErrUnsupportedFormat) {
					return fmt.Errorf("model binary specified in FROM field is not a valid gguf format model, %w", err)
				} else if err != nil {
					return err
460
				}
Michael Yang's avatar
Michael Yang committed
461

Michael Yang's avatar
Michael Yang committed
462
				config.SetModelFormat(ggml.Name())
Michael Yang's avatar
Michael Yang committed
463
464
465
				config.SetModelFamily(ggml.KV().Architecture())
				config.SetModelType(format.HumanNumber(ggml.KV().ParameterCount()))
				config.SetFileType(ggml.KV().FileType())
466

467
				mediatype := mediatype
Michael Yang's avatar
Michael Yang committed
468
				if ggml.KV().Architecture() == "clip" {
469
470
					mediatype = "application/vnd.ollama.image.projector"
				}
471

Michael Yang's avatar
Michael Yang committed
472
				sr := io.NewSectionReader(bin, offset, size)
473
474
475
476
477
478
479
				layer, err := NewLayer(sr, mediatype)
				if err != nil {
					return err
				}

				layers.Add(layer)

Michael Yang's avatar
Michael Yang committed
480
				offset += size
481
			}
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
			_, size, err := llm.DecodeGGML(bin)
Michael Yang's avatar
Michael Yang committed
500
501
502
503
			if err != nil {
				return err
			}

Michael Yang's avatar
Michael Yang committed
504
			sr := io.NewSectionReader(bin, 0, size)
Michael Yang's avatar
Michael Yang committed
505
			layer, err := NewLayer(sr, mediatype)
506
			if err != nil {
Michael Yang's avatar
Michael Yang committed
507
				return err
508
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
509

Michael Yang's avatar
Michael Yang committed
510
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
511
512
		case "license":
			fn(api.ProgressResponse{Status: "creating license layer"})
Michael Yang's avatar
Michael Yang committed
513
514
515

			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
Bruce MacDonald's avatar
Bruce MacDonald committed
516
517
518
519
			if err != nil {
				return err
			}

Michael Yang's avatar
Michael Yang committed
520
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
521
522
523
		case "template", "system":
			fn(api.ProgressResponse{Status: fmt.Sprintf("creating %s layer", c.Name)})

Michael Yang's avatar
Michael Yang committed
524
525
			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
526
			if err != nil {
527
				return err
528
			}
529

Michael Yang's avatar
Michael Yang committed
530
			layers.Replace(layer)
531
532
		case "message":
			messages = append(messages, c.Args)
533
		default:
534
			params[c.Name] = append(params[c.Name], c.Args)
535
536
537
		}
	}

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
	if len(messages) > 0 {
		fn(api.ProgressResponse{Status: "creating parameters layer"})

		msgs := make([]api.Message, 0)

		for _, m := range messages {
			// todo: handle images
			msg := strings.SplitN(m, ": ", 2)
			msgs = append(msgs, api.Message{Role: msg[0], Content: msg[1]})
		}

		var b bytes.Buffer
		if err := json.NewEncoder(&b).Encode(msgs); err != nil {
			return err
		}

		layer, err := NewLayer(&b, "application/vnd.ollama.image.messages")
		if err != nil {
			return err
		}

		layers.Replace(layer)
	}

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

565
		formattedParams, err := api.FormatParams(params)
566
		if err != nil {
Michael Yang's avatar
Michael Yang committed
567
			return err
568
		}
569

Michael Yang's avatar
Michael Yang committed
570
		for k, v := range fromParams {
Michael Yang's avatar
Michael Yang committed
571
572
573
574
575
			if _, ok := formattedParams[k]; !ok {
				formattedParams[k] = v
			}
		}

Michael Yang's avatar
Michael Yang committed
576
577
		var b bytes.Buffer
		if err := json.NewEncoder(&b).Encode(formattedParams); err != nil {
578
579
580
			return err
		}

Michael Yang's avatar
Michael Yang committed
581
		fn(api.ProgressResponse{Status: "creating config layer"})
Michael Yang's avatar
Michael Yang committed
582
		layer, err := NewLayer(&b, "application/vnd.ollama.image.params")
583
		if err != nil {
Michael Yang's avatar
Michael Yang committed
584
			return err
585
		}
Michael Yang's avatar
Michael Yang committed
586

Michael Yang's avatar
Michael Yang committed
587
		layers.Replace(layer)
588
589
	}

Michael Yang's avatar
Michael Yang committed
590
591
592
	digests := make([]string, len(layers.items))
	for i, layer := range layers.items {
		digests[i] = layer.Digest
593
594
	}

Michael Yang's avatar
Michael Yang committed
595
	config.RootFS.DiffIDs = digests
Michael Yang's avatar
Michael Yang committed
596

Michael Yang's avatar
Michael Yang committed
597
598
	var b bytes.Buffer
	if err := json.NewEncoder(&b).Encode(config); err != nil {
599
600
601
		return err
	}

Michael Yang's avatar
Michael Yang committed
602
603
	configLayer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json")
	if err != nil {
604
605
606
		return err
	}

Michael Yang's avatar
Michael Yang committed
607
	delete(deleteMap, configLayer.Digest)
608

Michael Yang's avatar
Michael Yang committed
609
610
	for _, layer := range append(layers.items, configLayer) {
		committed, err := layer.Commit()
611
612
613
		if err != nil {
			return err
		}
614

Michael Yang's avatar
Michael Yang committed
615
616
617
		status := "writing layer"
		if !committed {
			status = "using already created layer"
618
619
		}

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

Michael Yang's avatar
Michael Yang committed
622
		delete(deleteMap, layer.Digest)
623
624
	}

Michael Yang's avatar
Michael Yang committed
625
626
	fn(api.ProgressResponse{Status: "writing manifest"})
	if err := WriteManifest(name, configLayer, layers.items); err != nil {
627
628
		return err
	}
629

Michael Yang's avatar
Michael Yang committed
630
631
632
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := deleteUnusedLayers(nil, deleteMap, false); err != nil {
			return err
633
634
635
		}
	}

Michael Yang's avatar
Michael Yang committed
636
637
	fn(api.ProgressResponse{Status: "success"})
	return nil
638
639
}

640
func convertModel(name, path string, fn func(resp api.ProgressResponse)) (string, error) {
641
	r, err := zip.OpenReader(path)
642
643
644
645
646
647
648
649
650
651
652
	if err != nil {
		return "", err
	}
	defer r.Close()

	tempDir, err := os.MkdirTemp("", "ollama-convert")
	if err != nil {
		return "", err
	}
	defer os.RemoveAll(tempDir)

653
	fn(api.ProgressResponse{Status: "unpacking model metadata"})
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
	for _, f := range r.File {
		fpath := filepath.Join(tempDir, f.Name)
		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return "", err
		}

		rc, err := f.Open()
		if err != nil {
			return "", err
		}

		_, err = io.Copy(outFile, rc)
		if err != nil {
			return "", err
		}

		outFile.Close()
		rc.Close()
	}

675
	mf, err := convert.GetModelFormat(tempDir)
676
677
678
679
	if err != nil {
		return "", err
	}

680
	params, err := mf.GetParams(tempDir)
681
682
	if err != nil {
		return "", err
683
684
	}

685
686
687
688
689
690
	mArch, err := mf.GetModelArch(name, tempDir, params)
	if err != nil {
		return "", err
	}

	fn(api.ProgressResponse{Status: "processing tensors"})
691
	if err := mArch.GetTensors(); err != nil {
692
693
694
		return "", err
	}

695
	if err := mArch.LoadVocab(); err != nil {
696
697
698
		return "", err
	}

699
	fn(api.ProgressResponse{Status: "converting model"})
700
	path, err = mArch.WriteGGUF()
701
702
703
704
	if err != nil {
		return "", err
	}

705
	return path, nil
706
707
}

Michael Yang's avatar
Michael Yang committed
708
func CopyModel(src, dst model.Name) error {
709
710
711
712
713
714
715
	if !dst.IsFullyQualified() {
		return model.Unqualified(dst)
	}
	if !src.IsFullyQualified() {
		return model.Unqualified(src)
	}

716
717
718
719
	if src.Filepath() == dst.Filepath() {
		return nil
	}

Michael Yang's avatar
Michael Yang committed
720
	manifests, err := GetManifestPath()
721
722
723
724
	if err != nil {
		return err
	}

725
	dstpath := filepath.Join(manifests, dst.Filepath())
Michael Yang's avatar
Michael Yang committed
726
	if err := os.MkdirAll(filepath.Dir(dstpath), 0o755); err != nil {
727
728
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
729

730
	srcpath := filepath.Join(manifests, src.Filepath())
Michael Yang's avatar
Michael Yang committed
731
	srcfile, err := os.Open(srcpath)
Patrick Devine's avatar
Patrick Devine committed
732
733
734
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
735
	defer srcfile.Close()
Patrick Devine's avatar
Patrick Devine committed
736

Michael Yang's avatar
Michael Yang committed
737
	dstfile, err := os.Create(dstpath)
Patrick Devine's avatar
Patrick Devine committed
738
739
740
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
741
	defer dstfile.Close()
Patrick Devine's avatar
Patrick Devine committed
742

Michael Yang's avatar
Michael Yang committed
743
744
	_, err = io.Copy(dstfile, srcfile)
	return err
Patrick Devine's avatar
Patrick Devine committed
745
746
}

Michael Yang's avatar
Michael Yang committed
747
func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}, dryRun bool) error {
748
749
750
751
	fp, err := GetManifestPath()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
752
753
754
755

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

Michael Yang's avatar
Michael Yang committed
758
759
760
761
		dir, file := filepath.Split(path)
		dir = strings.Trim(strings.TrimPrefix(dir, fp), string(os.PathSeparator))
		tag := strings.Join([]string{dir, file}, ":")
		fmp := ParseModelPath(tag)
762

Michael Yang's avatar
Michael Yang committed
763
		// skip the manifest we're trying to delete
764
		if skipModelPath != nil && skipModelPath.GetFullTagname() == fmp.GetFullTagname() {
Michael Yang's avatar
Michael Yang committed
765
			return nil
766
		}
Michael Yang's avatar
Michael Yang committed
767
768
769
770

		// save (i.e. delete from the deleteMap) any files used in other manifests
		manifest, _, err := GetManifest(fmp)
		if err != nil {
Michael Yang's avatar
Michael Yang committed
771
			// nolint: nilerr
Michael Yang's avatar
Michael Yang committed
772
773
774
775
776
777
778
779
			return nil
		}

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

		delete(deleteMap, manifest.Config.Digest)
780
		return nil
Michael Yang's avatar
Michael Yang committed
781
782
783
	}

	if err := filepath.Walk(fp, walkFunc); err != nil {
Michael Yang's avatar
Michael Yang committed
784
785
		return err
	}
786
787

	// only delete the files which are still in the deleteMap
Michael Yang's avatar
Michael Yang committed
788
789
790
	for k := range deleteMap {
		fp, err := GetBlobsPath(k)
		if err != nil {
791
			slog.Info(fmt.Sprintf("couldn't get file path for '%s': %v", k, err))
Michael Yang's avatar
Michael Yang committed
792
793
794
795
			continue
		}
		if !dryRun {
			if err := os.Remove(fp); err != nil {
796
				slog.Info(fmt.Sprintf("couldn't remove file '%s': %v", fp, err))
797
798
				continue
			}
Michael Yang's avatar
Michael Yang committed
799
		} else {
800
			slog.Info(fmt.Sprintf("wanted to remove: %s", fp))
801
802
803
		}
	}

804
805
806
807
	return nil
}

func PruneLayers() error {
Michael Yang's avatar
Michael Yang committed
808
	deleteMap := make(map[string]struct{})
809
810
811
812
813
814
815
	p, err := GetBlobsPath("")
	if err != nil {
		return err
	}

	blobs, err := os.ReadDir(p)
	if err != nil {
816
		slog.Info(fmt.Sprintf("couldn't read dir '%s': %v", p, err))
817
818
819
820
821
		return err
	}

	for _, blob := range blobs {
		name := blob.Name()
822
		name = strings.ReplaceAll(name, "-", ":")
Michael Yang's avatar
Michael Yang committed
823
824
825
		if strings.HasPrefix(name, "sha256:") {
			deleteMap[name] = struct{}{}
		}
826
827
	}

828
	slog.Info(fmt.Sprintf("total blobs: %d", len(deleteMap)))
829
830
831
832
833
834

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

835
	slog.Info(fmt.Sprintf("total unused blobs removed: %d", len(deleteMap)))
836
837
838
839

	return nil
}

Michael Yang's avatar
Michael Yang committed
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
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
}

873
874
875
876
877
878
879
func DeleteModel(name string) error {
	mp := ParseModelPath(name)
	manifest, _, err := GetManifest(mp)
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
880
	deleteMap := make(map[string]struct{})
881
	for _, layer := range manifest.Layers {
Michael Yang's avatar
Michael Yang committed
882
		deleteMap[layer.Digest] = struct{}{}
883
	}
Michael Yang's avatar
Michael Yang committed
884
	deleteMap[manifest.Config.Digest] = struct{}{}
885
886
887
888
889
890

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

891
	fp, err := mp.GetManifestPath()
892
893
894
895
896
	if err != nil {
		return err
	}
	err = os.Remove(fp)
	if err != nil {
897
		slog.Info(fmt.Sprintf("couldn't remove manifest file '%s': %v", fp, err))
898
899
900
901
902
903
		return err
	}

	return nil
}

Patrick Devine's avatar
Patrick Devine committed
904
func ShowModelfile(model *Model) (string, error) {
Michael Yang's avatar
Michael Yang committed
905
	var mt struct {
Patrick Devine's avatar
Patrick Devine committed
906
		*Model
Michael Yang's avatar
Michael Yang committed
907
		From       string
Michael Yang's avatar
Michael Yang committed
908
		Parameters map[string][]any
Patrick Devine's avatar
Patrick Devine committed
909
910
	}

Michael Yang's avatar
Michael Yang committed
911
	mt.Parameters = make(map[string][]any)
Patrick Devine's avatar
Patrick Devine committed
912
	for k, v := range model.Options {
Michael Yang's avatar
Michael Yang committed
913
914
915
		if s, ok := v.([]any); ok {
			mt.Parameters[k] = s
			continue
Patrick Devine's avatar
Patrick Devine committed
916
917
		}

Michael Yang's avatar
Michael Yang committed
918
		mt.Parameters[k] = []any{v}
Patrick Devine's avatar
Patrick Devine committed
919
920
	}

Michael Yang's avatar
Michael Yang committed
921
922
	mt.Model = model
	mt.From = model.ModelPath
Patrick Devine's avatar
Patrick Devine committed
923

924
925
	if model.ParentModel != "" {
		mt.From = model.ParentModel
Patrick Devine's avatar
Patrick Devine committed
926
927
928
929
930
931
932
933
	}

	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 }}"""
934
935

{{- if .System }}
Patrick Devine's avatar
Patrick Devine committed
936
SYSTEM """{{ .System }}"""
937
{{- end }}
938
939
940
941

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

Michael Yang's avatar
Michael Yang committed
943
944
945
946
{{- range $k, $v := .Parameters }}
{{- range $parameter := $v }}
PARAMETER {{ $k }} {{ printf "%#v" $parameter }}
{{- end }}
Michael Yang's avatar
Michael Yang committed
947
{{- end }}`
Patrick Devine's avatar
Patrick Devine committed
948
949
950

	tmpl, err := template.New("").Parse(modelFile)
	if err != nil {
951
		slog.Info(fmt.Sprintf("error parsing template: %q", err))
Patrick Devine's avatar
Patrick Devine committed
952
953
954
955
956
957
		return "", err
	}

	var buf bytes.Buffer

	if err = tmpl.Execute(&buf, mt); err != nil {
958
		slog.Info(fmt.Sprintf("error executing template: %q", err))
Patrick Devine's avatar
Patrick Devine committed
959
960
961
962
963
964
		return "", err
	}

	return buf.String(), nil
}

Michael Yang's avatar
Michael Yang committed
965
func PushModel(ctx context.Context, name string, regOpts *registryOptions, fn func(api.ProgressResponse)) error {
966
	mp := ParseModelPath(name)
967
968
	fn(api.ProgressResponse{Status: "retrieving manifest"})

969
970
971
972
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
973
	manifest, _, err := GetManifest(mp)
974
	if err != nil {
975
		fn(api.ProgressResponse{Status: "couldn't retrieve manifest"})
976
977
978
979
		return err
	}

	var layers []*Layer
Jeffrey Morgan's avatar
Jeffrey Morgan committed
980
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
981
	layers = append(layers, manifest.Config)
982
983

	for _, layer := range layers {
Michael Yang's avatar
Michael Yang committed
984
		if err := uploadBlob(ctx, mp, layer, regOpts, fn); err != nil {
985
			slog.Info(fmt.Sprintf("error uploading blob: %v", err))
986
987
			return err
		}
988
989
	}

990
	fn(api.ProgressResponse{Status: "pushing manifest"})
Michael Yang's avatar
Michael Yang committed
991
992
	requestURL := mp.BaseURL()
	requestURL = requestURL.JoinPath("v2", mp.GetNamespaceRepository(), "manifests", mp.Tag)
993
994
995
996
997
998

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

Michael Yang's avatar
Michael Yang committed
999
1000
	headers := make(http.Header)
	headers.Set("Content-Type", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1001
	resp, err := makeRequestWithRetry(ctx, http.MethodPut, requestURL, headers, bytes.NewReader(manifestJSON), regOpts)
1002
1003
1004
1005
1006
	if err != nil {
		return err
	}
	defer resp.Body.Close()

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

	return nil
}

Michael Yang's avatar
Michael Yang committed
1012
func PullModel(ctx context.Context, name string, regOpts *registryOptions, fn func(api.ProgressResponse)) error {
1013
1014
	mp := ParseModelPath(name)

1015
1016
1017
1018
1019
	var manifest *ManifestV2
	var err error
	var noprune string

	// build deleteMap to prune unused layers
Michael Yang's avatar
Michael Yang committed
1020
	deleteMap := make(map[string]struct{})
1021
1022
1023
1024
1025
1026
1027
1028
1029

	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
1030
				deleteMap[l.Digest] = struct{}{}
1031
			}
Michael Yang's avatar
Michael Yang committed
1032
			deleteMap[manifest.Config.Digest] = struct{}{}
1033
1034
1035
		}
	}

1036
1037
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
1038
	}
1039

1040
	fn(api.ProgressResponse{Status: "pulling manifest"})
1041

1042
	manifest, err = pullModelManifest(ctx, mp, regOpts)
1043
	if err != nil {
1044
		return fmt.Errorf("pull model manifest: %s", err)
1045
1046
1047
	}

	var layers []*Layer
Bruce MacDonald's avatar
Bruce MacDonald committed
1048
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
1049
	layers = append(layers, manifest.Config)
1050
1051

	for _, layer := range layers {
1052
1053
1054
1055
1056
1057
1058
1059
		if err := downloadBlob(
			ctx,
			downloadOpts{
				mp:      mp,
				digest:  layer.Digest,
				regOpts: regOpts,
				fn:      fn,
			}); err != nil {
1060
1061
			return err
		}
1062
		delete(deleteMap, layer.Digest)
1063
	}
1064
	delete(deleteMap, manifest.Config.Digest)
1065

Michael Yang's avatar
Michael Yang committed
1066
1067
1068
	fn(api.ProgressResponse{Status: "verifying sha256 digest"})
	for _, layer := range layers {
		if err := verifyBlob(layer.Digest); err != nil {
1069
1070
1071
1072
1073
1074
1075
1076
			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
1077
					slog.Info(fmt.Sprintf("couldn't remove file with digest mismatch '%s': %v", fp, err))
1078
1079
				}
			}
Michael Yang's avatar
Michael Yang committed
1080
1081
1082
1083
			return err
		}
	}

1084
	fn(api.ProgressResponse{Status: "writing manifest"})
1085

1086
	manifestJSON, err := json.Marshal(manifest)
1087
1088
1089
1090
	if err != nil {
		return err
	}

1091
	fp, err := mp.GetManifestPath()
1092
1093
1094
	if err != nil {
		return err
	}
1095
1096
1097
	if err := os.MkdirAll(filepath.Dir(fp), 0o755); err != nil {
		return err
	}
1098

Bruce MacDonald's avatar
Bruce MacDonald committed
1099
	err = os.WriteFile(fp, manifestJSON, 0o644)
1100
	if err != nil {
1101
		slog.Info(fmt.Sprintf("couldn't write to %s", fp))
1102
1103
1104
		return err
	}

1105
1106
1107
1108
1109
1110
1111
1112
	if noprune == "" {
		fn(api.ProgressResponse{Status: "removing any unused layers"})
		err = deleteUnusedLayers(nil, deleteMap, false)
		if err != nil {
			return err
		}
	}

1113
	fn(api.ProgressResponse{Status: "success"})
1114
1115
1116
1117

	return nil
}

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

Michael Yang's avatar
Michael Yang committed
1121
1122
	headers := make(http.Header)
	headers.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1123
	resp, err := makeRequestWithRetry(ctx, http.MethodGet, requestURL, headers, nil, regOpts)
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
	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
1138
func GetSHA256Digest(r io.Reader) (string, int64) {
Michael Yang's avatar
Michael Yang committed
1139
1140
1141
1142
1143
1144
	h := sha256.New()
	n, err := io.Copy(h, r)
	if err != nil {
		log.Fatal(err)
	}

Michael Yang's avatar
Michael Yang committed
1145
	return fmt.Sprintf("sha256:%x", h.Sum(nil)), n
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
1176
1177
1178
var errUnauthorized = fmt.Errorf("unauthorized: access denied")

// getTokenSubject returns the subject of a JWT token, it does not validate the token
func getTokenSubject(token string) string {
	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		slog.Error("jwt token does not contain 3 parts")
		return ""
	}

	payload := parts[1]
	payloadBytes, err := base64.RawURLEncoding.DecodeString(payload)
	if err != nil {
		slog.Error(fmt.Sprintf("failed to decode jwt payload: %v", err))
		return ""
	}

	var payloadMap map[string]interface{}
	if err := json.Unmarshal(payloadBytes, &payloadMap); err != nil {
		slog.Error(fmt.Sprintf("failed to unmarshal payload JSON: %v", err))
		return ""
	}

	sub, ok := payloadMap["sub"]
	if !ok {
		slog.Error("jwt does not contain 'sub' field")
		return ""
	}

	return fmt.Sprintf("%s", sub)
}
1179

Michael Yang's avatar
Michael Yang committed
1180
func makeRequestWithRetry(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.ReadSeeker, regOpts *registryOptions) (*http.Response, error) {
1181
	anonymous := true // access will default to anonymous if no user is found associated with the public key
Michael Yang's avatar
Michael Yang committed
1182
	for i := 0; i < 2; i++ {
Michael Yang's avatar
Michael Yang committed
1183
		resp, err := makeRequest(ctx, method, requestURL, headers, body, regOpts)
Michael Yang's avatar
Michael Yang committed
1184
		if err != nil {
Michael Yang's avatar
Michael Yang committed
1185
			if !errors.Is(err, context.Canceled) {
1186
				slog.Info(fmt.Sprintf("request failed: %v", err))
Michael Yang's avatar
Michael Yang committed
1187
1188
			}

Michael Yang's avatar
Michael Yang committed
1189
1190
			return nil, err
		}
Michael Yang's avatar
Michael Yang committed
1191
1192
1193
1194

		switch {
		case resp.StatusCode == http.StatusUnauthorized:
			// Handle authentication error with one retry
Michael Yang's avatar
Michael Yang committed
1195
1196
			challenge := parseRegistryChallenge(resp.Header.Get("www-authenticate"))
			token, err := getAuthorizationToken(ctx, challenge)
Michael Yang's avatar
Michael Yang committed
1197
1198
1199
			if err != nil {
				return nil, err
			}
1200
			anonymous = getTokenSubject(token) == "anonymous"
Michael Yang's avatar
Michael Yang committed
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
			regOpts.Token = token
			if body != nil {
				_, err = body.Seek(0, io.SeekStart)
				if err != nil {
					return nil, err
				}
			}
		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)
		default:
			return resp, nil
Michael Yang's avatar
Michael Yang committed
1218
1219
1220
		}
	}

1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
	if anonymous {
		// no user is associated with the public key, and the request requires non-anonymous access
		pubKey, nestedErr := auth.GetPublicKey()
		if nestedErr != nil {
			slog.Error(fmt.Sprintf("couldn't get public key: %v", nestedErr))
			return nil, errUnauthorized
		}
		return nil, &errtypes.UnknownOllamaKey{Key: pubKey}
	}
	// user is associated with the public key, but is not authorized to make the request
Michael Yang's avatar
Michael Yang committed
1231
	return nil, errUnauthorized
Michael Yang's avatar
Michael Yang committed
1232
1233
}

Michael Yang's avatar
Michael Yang committed
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
func makeRequest(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.Reader, regOpts *registryOptions) (*http.Response, error) {
	if requestURL.Scheme != "http" && regOpts != nil && regOpts.Insecure {
		requestURL.Scheme = "http"
	}

	req, err := http.NewRequestWithContext(ctx, method, requestURL.String(), body)
	if err != nil {
		return nil, err
	}

	if headers != nil {
		req.Header = headers
	}

	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)
		}
	}

	req.Header.Set("User-Agent", fmt.Sprintf("ollama/%s (%s %s) Go/%s", version.Version, runtime.GOARCH, runtime.GOOS, runtime.Version()))

	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
1267
	resp, err := http.DefaultClient.Do(req)
Michael Yang's avatar
Michael Yang committed
1268
1269
1270
1271
1272
1273
1274
	if err != nil {
		return nil, err
	}

	return resp, nil
}

Patrick Devine's avatar
Patrick Devine committed
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
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]
}

Michael Yang's avatar
Michael Yang committed
1298
func parseRegistryChallenge(authStr string) registryChallenge {
Patrick Devine's avatar
Patrick Devine committed
1299
1300
	authStr = strings.TrimPrefix(authStr, "Bearer ")

Michael Yang's avatar
Michael Yang committed
1301
	return registryChallenge{
Patrick Devine's avatar
Patrick Devine committed
1302
1303
1304
1305
1306
1307
		Realm:   getValue(authStr, "realm"),
		Service: getValue(authStr, "service"),
		Scope:   getValue(authStr, "scope"),
	}
}

1308
var errDigestMismatch = errors.New("digest mismatch, file must be downloaded again")
1309

Michael Yang's avatar
Michael Yang committed
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
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 {
1324
		return fmt.Errorf("%w: want %s, got %s", errDigestMismatch, digest, fileDigest)
Michael Yang's avatar
Michael Yang committed
1325
1326
1327
1328
	}

	return nil
}