images.go 30.5 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
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
13
	"log/slog"
14
	"net/http"
Michael Yang's avatar
Michael Yang committed
15
	"net/url"
16
17
	"os"
	"path/filepath"
Michael Yang's avatar
Michael Yang committed
18
	"runtime"
19
20
	"strconv"
	"strings"
Quinn Slack's avatar
Quinn Slack committed
21
	"text/template"
22
	"text/template/parse"
23

Michael Yang's avatar
Michael Yang committed
24
25
	"golang.org/x/exp/slices"

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

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

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

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
59
60
}

Bruce MacDonald's avatar
Bruce MacDonald committed
61
62
63
64
65
66
type PromptVars struct {
	System   string
	Prompt   string
	Response string
	First    bool
}
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// extractParts extracts the parts of the template before and after the {{.Response}} node.
func extractParts(tmplStr string) (pre string, post string, err error) {
	tmpl, err := template.New("").Parse(tmplStr)
	if err != nil {
		return "", "", err
	}

	var foundResponse bool

	for _, node := range tmpl.Tree.Root.Nodes {
		if node.Type() == parse.NodeAction && node.String() == "{{.Response}}" {
			foundResponse = true
		}
		if !foundResponse {
			pre += node.String()
		} else {
			post += node.String()
		}
	}

	return pre, post, nil
}

func Prompt(promptTemplate string, p PromptVars) (string, error) {
Bruce MacDonald's avatar
Bruce MacDonald committed
92
	var prompt strings.Builder
93
	// Use the "missingkey=zero" option to handle missing variables without panicking
94
	tmpl, err := template.New("").Option("missingkey=zero").Parse(promptTemplate)
95
96
97
98
	if err != nil {
		return "", err
	}

99
100
101
102
103
	vars := map[string]any{
		"System":   p.System,
		"Prompt":   p.Prompt,
		"Response": p.Response,
		"First":    p.First,
104
105
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
106
	var sb strings.Builder
107
	if err := tmpl.Execute(&sb, vars); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
108
109
110
		return "", err
	}
	prompt.WriteString(sb.String())
111
112
113
114
115
116

	if !strings.Contains(prompt.String(), p.Response) {
		// if the response is not in the prompt template, append it to the end
		prompt.WriteString(p.Response)
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
117
118
	return prompt.String(), nil
}
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// PreResponsePrompt returns the prompt before the response tag
func (m *Model) PreResponsePrompt(p PromptVars) (string, error) {
	pre, _, err := extractParts(m.Template)
	if err != nil {
		return "", err
	}

	return Prompt(pre, p)
}

// PostResponseTemplate returns the template after the response tag
func (m *Model) PostResponseTemplate(p PromptVars) (string, error) {
	if p.System == "" {
		// use the default system prompt for this model if one is not specified
		p.System = m.System
	}
	_, post, err := extractParts(m.Template)
	if err != nil {
		return "", err
	}

	if post == "" {
		// if there is no post-response template, return the provided response
		return p.Response, nil
	}

	return Prompt(post, p)
}

149
150
151
152
153
154
155
156
type ChatHistory struct {
	Prompts       []PromptVars
	CurrentImages []api.ImageData
	LastSystem    string
}

// ChatPrompts returns a list of formatted chat prompts from a list of messages
func (m *Model) ChatPrompts(msgs []api.Message) (*ChatHistory, error) {
Bruce MacDonald's avatar
Bruce MacDonald committed
157
	// build the prompt from the list of messages
158
	var currentImages []api.ImageData
159
	lastSystem := m.System
Bruce MacDonald's avatar
Bruce MacDonald committed
160
	currentVars := PromptVars{
161
162
		First:  true,
		System: m.System,
Bruce MacDonald's avatar
Bruce MacDonald committed
163
164
	}

165
	prompts := []PromptVars{}
Bruce MacDonald's avatar
Bruce MacDonald committed
166
167

	for _, msg := range msgs {
168
		switch strings.ToLower(msg.Role) {
Bruce MacDonald's avatar
Bruce MacDonald committed
169
		case "system":
170
171
			// if this is the first message it overrides the system prompt in the modelfile
			if !currentVars.First && currentVars.System != "" {
172
173
				prompts = append(prompts, currentVars)
				currentVars = PromptVars{}
Bruce MacDonald's avatar
Bruce MacDonald committed
174
175
			}
			currentVars.System = msg.Content
176
			lastSystem = msg.Content
Bruce MacDonald's avatar
Bruce MacDonald committed
177
		case "user":
178
			if currentVars.Prompt != "" {
179
180
				prompts = append(prompts, currentVars)
				currentVars = PromptVars{}
Bruce MacDonald's avatar
Bruce MacDonald committed
181
			}
182

Bruce MacDonald's avatar
Bruce MacDonald committed
183
			currentVars.Prompt = msg.Content
184
185
186
187
188
			for i := range msg.Images {
				currentVars.Prompt += fmt.Sprintf(" [img-%d]", len(currentImages)+i)
			}

			currentImages = append(currentImages, msg.Images...)
Bruce MacDonald's avatar
Bruce MacDonald committed
189
190
		case "assistant":
			currentVars.Response = msg.Content
191
192
			prompts = append(prompts, currentVars)
			currentVars = PromptVars{}
Bruce MacDonald's avatar
Bruce MacDonald committed
193
		default:
194
			return nil, fmt.Errorf("invalid role: %s, role must be one of [system, user, assistant]", msg.Role)
Bruce MacDonald's avatar
Bruce MacDonald committed
195
		}
196
197
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
198
199
	// Append the last set of vars if they are non-empty
	if currentVars.Prompt != "" || currentVars.System != "" {
200
		prompts = append(prompts, currentVars)
Bruce MacDonald's avatar
Bruce MacDonald committed
201
202
	}

203
204
205
206
207
	return &ChatHistory{
		Prompts:       prompts,
		CurrentImages: currentImages,
		LastSystem:    lastSystem,
	}, nil
208
209
}

210
211
212
type ManifestV2 struct {
	SchemaVersion int      `json:"schemaVersion"`
	MediaType     string   `json:"mediaType"`
Michael Yang's avatar
Michael Yang committed
213
	Config        *Layer   `json:"config"`
214
215
216
217
	Layers        []*Layer `json:"layers"`
}

type ConfigV2 struct {
218
219
220
221
222
223
	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"`

224
	// required by spec
225
226
	Architecture string `json:"architecture"`
	OS           string `json:"os"`
227
	RootFS       RootFS `json:"rootfs"`
228
229
}

Michael Yang's avatar
Michael Yang committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
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
	}
}

260
261
262
263
264
type RootFS struct {
	Type    string   `json:"type"`
	DiffIDs []string `json:"diff_ids"`
}

Michael Yang's avatar
Michael Yang committed
265
func (m *ManifestV2) GetTotalSize() (total int64) {
Patrick Devine's avatar
Patrick Devine committed
266
267
268
	for _, layer := range m.Layers {
		total += layer.Size
	}
Michael Yang's avatar
Michael Yang committed
269

Patrick Devine's avatar
Patrick Devine committed
270
271
272
273
	total += m.Config.Size
	return total
}

Patrick Devine's avatar
Patrick Devine committed
274
func GetManifest(mp ModelPath) (*ManifestV2, string, error) {
275
	fp, err := mp.GetManifestPath()
276
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
277
		return nil, "", err
278
	}
279

280
	if _, err = os.Stat(fp); err != nil {
Patrick Devine's avatar
Patrick Devine committed
281
		return nil, "", err
282
283
284
285
	}

	var manifest *ManifestV2

286
	bts, err := os.ReadFile(fp)
287
	if err != nil {
Patrick Devine's avatar
Patrick Devine committed
288
		return nil, "", fmt.Errorf("couldn't open file '%s'", fp)
289
290
	}

Patrick Devine's avatar
Patrick Devine committed
291
292
293
	shaSum := sha256.Sum256(bts)
	shaStr := hex.EncodeToString(shaSum[:])

294
	if err := json.Unmarshal(bts, &manifest); err != nil {
Patrick Devine's avatar
Patrick Devine committed
295
		return nil, "", err
296
297
	}

Patrick Devine's avatar
Patrick Devine committed
298
	return manifest, shaStr, nil
299
300
301
}

func GetModel(name string) (*Model, error) {
302
	mp := ParseModelPath(name)
Patrick Devine's avatar
Patrick Devine committed
303
	manifest, digest, err := GetManifest(mp)
304
305
306
307
308
	if err != nil {
		return nil, err
	}

	model := &Model{
309
310
311
312
313
		Name:      mp.GetFullTagname(),
		ShortName: mp.GetShortTagname(),
		Digest:    digest,
		Template:  "{{ .Prompt }}",
		License:   []string{},
Patrick Devine's avatar
Patrick Devine committed
314
		Size:      manifest.GetTotalSize(),
315
316
	}

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
	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
	}

332
	for _, layer := range manifest.Layers {
Patrick Devine's avatar
Patrick Devine committed
333
		filename, err := GetBlobsPath(layer.Digest)
334
335
336
337
		if err != nil {
			return nil, err
		}

338
339
340
		switch layer.MediaType {
		case "application/vnd.ollama.image.model":
			model.ModelPath = filename
341
			model.ParentModel = layer.From
342
		case "application/vnd.ollama.image.embed":
343
344
			// Deprecated in versions  > 0.1.2
			// TODO: remove this warning in a future version
345
			slog.Info("WARNING: model contains embeddings, but embeddings in modelfiles have been deprecated and will be ignored.")
346
347
		case "application/vnd.ollama.image.adapter":
			model.AdapterPaths = append(model.AdapterPaths, filename)
Michael Yang's avatar
Michael Yang committed
348
349
		case "application/vnd.ollama.image.projector":
			model.ProjectorPaths = append(model.ProjectorPaths, filename)
350
351
352
353
354
355
356
357
358
		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)
359
360
361
			if err != nil {
				return nil, err
			}
362
363

			model.System = string(bts)
364
365
366
367
368
369
370
		case "application/vnd.ollama.image.prompt":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}

			model.Template = string(bts)
371
		case "application/vnd.ollama.image.params":
Michael Yang's avatar
Michael Yang committed
372
373
374
375
376
			params, err := os.Open(filename)
			if err != nil {
				return nil, err
			}
			defer params.Close()
377

378
			// parse model options parameters into a map so that we can see which fields have been specified explicitly
379
			if err = json.NewDecoder(params).Decode(&model.Options); err != nil {
380
381
				return nil, err
			}
382
383
384
385
386
387
388
389
390
391
		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
392
393
394
395
396
397
		case "application/vnd.ollama.image.license":
			bts, err := os.ReadFile(filename)
			if err != nil {
				return nil, err
			}
			model.License = append(model.License, string(bts))
398
399
400
401
402
403
		}
	}

	return model, nil
}

404
405
func realpath(mfDir, from string) string {
	abspath, err := filepath.Abs(from)
Michael Yang's avatar
Michael Yang committed
406
	if err != nil {
407
		return from
408
409
	}

Michael Yang's avatar
Michael Yang committed
410
	home, err := os.UserHomeDir()
411
	if err != nil {
Michael Yang's avatar
Michael Yang committed
412
		return abspath
413
414
	}

415
	if from == "~" {
Michael Yang's avatar
Michael Yang committed
416
		return home
417
418
419
420
421
422
423
	} 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)
424
425
	}

Michael Yang's avatar
Michael Yang committed
426
427
428
	return abspath
}

429
func CreateModel(ctx context.Context, name, modelFileDir string, commands []parser.Command, fn func(resp api.ProgressResponse)) error {
430
431
432
433
434
435
436
	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{}{}
		}
	}

437
438
	config := ConfigV2{
		OS:           "linux",
Michael Yang's avatar
Michael Yang committed
439
		Architecture: "amd64",
Michael Yang's avatar
Michael Yang committed
440
441
442
		RootFS: RootFS{
			Type: "layers",
		},
443
444
	}

Michael Yang's avatar
Michael Yang committed
445
	var layers Layers
446
	messages := []string{}
Michael Yang's avatar
Michael Yang committed
447

448
	params := make(map[string][]string)
Michael Yang's avatar
Michael Yang committed
449
450
	fromParams := make(map[string]any)

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

454
455
		switch c.Name {
		case "model":
Michael Yang's avatar
Michael Yang committed
456
457
458
459
460
461
462
463
464
			if strings.HasPrefix(c.Args, "@") {
				blobPath, err := GetBlobsPath(strings.TrimPrefix(c.Args, "@"))
				if err != nil {
					return err
				}

				c.Args = blobPath
			}

465
			bin, err := os.Open(realpath(modelFileDir, c.Args))
466
			if err != nil {
Michael Yang's avatar
Michael Yang committed
467
468
469
470
471
472
473
				// 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 {
474
475
476
						return err
					}

Michael Yang's avatar
Michael Yang committed
477
					manifest, _, err = GetManifest(modelpath)
478
479
480
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
481
482
				case err != nil:
					return err
483
				}
484

485
				fn(api.ProgressResponse{Status: "reading model metadata"})
Michael Yang's avatar
Michael Yang committed
486
				fromConfigPath, err := GetBlobsPath(manifest.Config.Digest)
Michael Yang's avatar
Michael Yang committed
487
488
489
490
				if err != nil {
					return err
				}

Michael Yang's avatar
Michael Yang committed
491
				fromConfigFile, err := os.Open(fromConfigPath)
Michael Yang's avatar
Michael Yang committed
492
493
494
				if err != nil {
					return err
				}
Michael Yang's avatar
Michael Yang committed
495
				defer fromConfigFile.Close()
Michael Yang's avatar
Michael Yang committed
496

Michael Yang's avatar
Michael Yang committed
497
498
				var fromConfig ConfigV2
				if err := json.NewDecoder(fromConfigFile).Decode(&fromConfig); err != nil {
Michael Yang's avatar
Michael Yang committed
499
500
501
					return err
				}

Bruce MacDonald's avatar
Bruce MacDonald committed
502
503
504
505
506
				// 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
507
508
509
510
				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
511

Michael Yang's avatar
Michael Yang committed
512
513
514
515
				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
516
517
518
519
						if err != nil {
							return err
						}

Michael Yang's avatar
Michael Yang committed
520
						fromParamsFile, err := os.Open(fromParamsPath)
Michael Yang's avatar
Michael Yang committed
521
522
523
						if err != nil {
							return err
						}
Michael Yang's avatar
Michael Yang committed
524
						defer fromParamsFile.Close()
Michael Yang's avatar
Michael Yang committed
525

Michael Yang's avatar
Michael Yang committed
526
						if err := json.NewDecoder(fromParamsFile).Decode(&fromParams); err != nil {
Michael Yang's avatar
Michael Yang committed
527
528
529
530
							return err
						}
					}

Michael Yang's avatar
Michael Yang committed
531
					layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, modelpath.GetShortTagname())
532
533
534
					if err != nil {
						return err
					}
Michael Yang's avatar
Michael Yang committed
535

Michael Yang's avatar
Michael Yang committed
536
					layers.Add(layer)
537
				}
Michael Yang's avatar
Michael Yang committed
538
539
540

				deleteMap[manifest.Config.Digest] = struct{}{}
				continue
541
			}
Michael Yang's avatar
Michael Yang committed
542
			defer bin.Close()
543

544
			var offset int64
Bruce MacDonald's avatar
Bruce MacDonald committed
545
		CREATE:
546
547
			for {
				fn(api.ProgressResponse{Status: "creating model layer"})
548

549
550
				bin.Seek(offset, io.SeekStart)
				ggml, err := llm.DecodeGGML(bin)
Bruce MacDonald's avatar
Bruce MacDonald committed
551
552
553
554
555
556
557
558
559
				if err != nil {
					switch {
					case errors.Is(err, io.EOF):
						break CREATE
					case errors.Is(err, llm.ErrUnsupportedFormat):
						return fmt.Errorf("model binary specified in FROM field is not a valid gguf format model, %w", err)
					default:
						return err
					}
560
				}
Michael Yang's avatar
Michael Yang committed
561

Michael Yang's avatar
Michael Yang committed
562
563
564
565
				config.SetModelFormat(ggml.Name())
				config.SetModelFamily(ggml.ModelFamily())
				config.SetModelType(ggml.ModelType())
				config.SetFileType(ggml.FileType())
566

567
568
569
570
				mediatype := mediatype
				if ggml.ModelFamily() == "clip" {
					mediatype = "application/vnd.ollama.image.projector"
				}
571

572
573
574
575
576
577
578
579
580
581
				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
582
		case "adapter":
583
584
585
586
587
588
589
590
			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
591

Michael Yang's avatar
Michael Yang committed
592
			fn(api.ProgressResponse{Status: "creating adapter layer"})
593
			bin, err := os.Open(realpath(modelFileDir, c.Args))
594
			if err != nil {
Michael Yang's avatar
Michael Yang committed
595
				return err
596
			}
Michael Yang's avatar
Michael Yang committed
597
			defer bin.Close()
598

Michael Yang's avatar
Michael Yang committed
599
			layer, err := NewLayer(bin, mediatype)
600
			if err != nil {
Michael Yang's avatar
Michael Yang committed
601
				return err
602
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
603

Michael Yang's avatar
Michael Yang committed
604
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
605
606
		case "license":
			fn(api.ProgressResponse{Status: "creating license layer"})
Michael Yang's avatar
Michael Yang committed
607
608
609

			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
Bruce MacDonald's avatar
Bruce MacDonald committed
610
611
612
613
			if err != nil {
				return err
			}

Michael Yang's avatar
Michael Yang committed
614
			layers.Add(layer)
Michael Yang's avatar
Michael Yang committed
615
616
617
		case "template", "system":
			fn(api.ProgressResponse{Status: fmt.Sprintf("creating %s layer", c.Name)})

Michael Yang's avatar
Michael Yang committed
618
619
			bin := strings.NewReader(c.Args)
			layer, err := NewLayer(bin, mediatype)
620
			if err != nil {
621
				return err
622
			}
623

Michael Yang's avatar
Michael Yang committed
624
			layers.Replace(layer)
625
626
		case "message":
			messages = append(messages, c.Args)
627
		default:
628
			params[c.Name] = append(params[c.Name], c.Args)
629
630
631
		}
	}

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
	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
656
	if len(params) > 0 {
Michael Yang's avatar
Michael Yang committed
657
		fn(api.ProgressResponse{Status: "creating parameters layer"})
Michael Yang's avatar
Michael Yang committed
658

659
		formattedParams, err := api.FormatParams(params)
660
		if err != nil {
Michael Yang's avatar
Michael Yang committed
661
			return err
662
		}
663

Michael Yang's avatar
Michael Yang committed
664
		for k, v := range fromParams {
Michael Yang's avatar
Michael Yang committed
665
666
667
668
669
			if _, ok := formattedParams[k]; !ok {
				formattedParams[k] = v
			}
		}

Patrick Devine's avatar
Patrick Devine committed
670
		// xxx - can this be removed?
Michael Yang's avatar
Michael Yang committed
671
		if config.ModelType == "65B" {
Michael Yang's avatar
Michael Yang committed
672
			if gqa, ok := formattedParams["gqa"].(int); ok && gqa == 8 {
Michael Yang's avatar
Michael Yang committed
673
674
675
676
				config.ModelType = "70B"
			}
		}

Michael Yang's avatar
Michael Yang committed
677
678
		var b bytes.Buffer
		if err := json.NewEncoder(&b).Encode(formattedParams); err != nil {
679
680
681
			return err
		}

Michael Yang's avatar
Michael Yang committed
682
		fn(api.ProgressResponse{Status: "creating config layer"})
Michael Yang's avatar
Michael Yang committed
683
		layer, err := NewLayer(&b, "application/vnd.ollama.image.params")
684
		if err != nil {
Michael Yang's avatar
Michael Yang committed
685
			return err
686
		}
Michael Yang's avatar
Michael Yang committed
687

Michael Yang's avatar
Michael Yang committed
688
		layers.Replace(layer)
689
690
	}

Michael Yang's avatar
Michael Yang committed
691
692
693
	digests := make([]string, len(layers.items))
	for i, layer := range layers.items {
		digests[i] = layer.Digest
694
695
	}

Michael Yang's avatar
Michael Yang committed
696
	config.RootFS.DiffIDs = digests
Michael Yang's avatar
Michael Yang committed
697

Michael Yang's avatar
Michael Yang committed
698
699
	var b bytes.Buffer
	if err := json.NewEncoder(&b).Encode(config); err != nil {
700
701
702
		return err
	}

Michael Yang's avatar
Michael Yang committed
703
704
	configLayer, err := NewLayer(&b, "application/vnd.docker.container.image.v1+json")
	if err != nil {
705
706
707
		return err
	}

Michael Yang's avatar
Michael Yang committed
708
	delete(deleteMap, configLayer.Digest)
709

Michael Yang's avatar
Michael Yang committed
710
711
	for _, layer := range append(layers.items, configLayer) {
		committed, err := layer.Commit()
712
713
714
		if err != nil {
			return err
		}
715

Michael Yang's avatar
Michael Yang committed
716
717
718
		status := "writing layer"
		if !committed {
			status = "using already created layer"
719
720
		}

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

Michael Yang's avatar
Michael Yang committed
723
		delete(deleteMap, layer.Digest)
724
725
	}

Michael Yang's avatar
Michael Yang committed
726
727
	fn(api.ProgressResponse{Status: "writing manifest"})
	if err := WriteManifest(name, configLayer, layers.items); err != nil {
728
729
		return err
	}
730

Michael Yang's avatar
Michael Yang committed
731
732
733
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := deleteUnusedLayers(nil, deleteMap, false); err != nil {
			return err
734
735
736
		}
	}

Michael Yang's avatar
Michael Yang committed
737
738
	fn(api.ProgressResponse{Status: "success"})
	return nil
739
740
}

Patrick Devine's avatar
Patrick Devine committed
741
func CopyModel(src, dest string) error {
742
	srcModelPath := ParseModelPath(src)
743
	srcPath, err := srcModelPath.GetManifestPath()
744
745
746
747
	if err != nil {
		return err
	}

748
	destModelPath := ParseModelPath(dest)
749
	destPath, err := destModelPath.GetManifestPath()
Patrick Devine's avatar
Patrick Devine committed
750
751
752
	if err != nil {
		return err
	}
753
754
755
	if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
756
757

	// copy the file
Michael Yang's avatar
Michael Yang committed
758
	input, err := os.ReadFile(srcPath)
Patrick Devine's avatar
Patrick Devine committed
759
760
761
762
763
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

Michael Yang's avatar
Michael Yang committed
764
	err = os.WriteFile(destPath, input, 0o644)
Patrick Devine's avatar
Patrick Devine committed
765
766
767
768
769
770
771
772
	if err != nil {
		fmt.Println("Error reading file:", err)
		return err
	}

	return nil
}

Michael Yang's avatar
Michael Yang committed
773
func deleteUnusedLayers(skipModelPath *ModelPath, deleteMap map[string]struct{}, dryRun bool) error {
774
775
776
777
	fp, err := GetManifestPath()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
778
779
780
781

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

Michael Yang's avatar
Michael Yang committed
784
785
786
787
		dir, file := filepath.Split(path)
		dir = strings.Trim(strings.TrimPrefix(dir, fp), string(os.PathSeparator))
		tag := strings.Join([]string{dir, file}, ":")
		fmp := ParseModelPath(tag)
788

Michael Yang's avatar
Michael Yang committed
789
		// skip the manifest we're trying to delete
790
		if skipModelPath != nil && skipModelPath.GetFullTagname() == fmp.GetFullTagname() {
Michael Yang's avatar
Michael Yang committed
791
			return nil
792
		}
Michael Yang's avatar
Michael Yang committed
793
794
795
796

		// 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
797
			// nolint: nilerr
Michael Yang's avatar
Michael Yang committed
798
799
800
801
802
803
804
805
			return nil
		}

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

		delete(deleteMap, manifest.Config.Digest)
806
		return nil
Michael Yang's avatar
Michael Yang committed
807
808
809
	}

	if err := filepath.Walk(fp, walkFunc); err != nil {
Michael Yang's avatar
Michael Yang committed
810
811
		return err
	}
812
813

	// only delete the files which are still in the deleteMap
Michael Yang's avatar
Michael Yang committed
814
815
816
	for k := range deleteMap {
		fp, err := GetBlobsPath(k)
		if err != nil {
817
			slog.Info(fmt.Sprintf("couldn't get file path for '%s': %v", k, err))
Michael Yang's avatar
Michael Yang committed
818
819
820
821
			continue
		}
		if !dryRun {
			if err := os.Remove(fp); err != nil {
822
				slog.Info(fmt.Sprintf("couldn't remove file '%s': %v", fp, err))
823
824
				continue
			}
Michael Yang's avatar
Michael Yang committed
825
		} else {
826
			slog.Info(fmt.Sprintf("wanted to remove: %s", fp))
827
828
829
		}
	}

830
831
832
833
	return nil
}

func PruneLayers() error {
Michael Yang's avatar
Michael Yang committed
834
	deleteMap := make(map[string]struct{})
835
836
837
838
839
840
841
	p, err := GetBlobsPath("")
	if err != nil {
		return err
	}

	blobs, err := os.ReadDir(p)
	if err != nil {
842
		slog.Info(fmt.Sprintf("couldn't read dir '%s': %v", p, err))
843
844
845
846
847
848
849
850
		return err
	}

	for _, blob := range blobs {
		name := blob.Name()
		if runtime.GOOS == "windows" {
			name = strings.ReplaceAll(name, "-", ":")
		}
Michael Yang's avatar
Michael Yang committed
851
852
853
		if strings.HasPrefix(name, "sha256:") {
			deleteMap[name] = struct{}{}
		}
854
855
	}

856
	slog.Info(fmt.Sprintf("total blobs: %d", len(deleteMap)))
857
858
859
860
861
862

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

863
	slog.Info(fmt.Sprintf("total unused blobs removed: %d", len(deleteMap)))
864
865
866
867

	return nil
}

Michael Yang's avatar
Michael Yang committed
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
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
}

901
902
903
904
905
906
907
func DeleteModel(name string) error {
	mp := ParseModelPath(name)
	manifest, _, err := GetManifest(mp)
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
908
	deleteMap := make(map[string]struct{})
909
	for _, layer := range manifest.Layers {
Michael Yang's avatar
Michael Yang committed
910
		deleteMap[layer.Digest] = struct{}{}
911
	}
Michael Yang's avatar
Michael Yang committed
912
	deleteMap[manifest.Config.Digest] = struct{}{}
913
914
915
916
917
918

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

919
	fp, err := mp.GetManifestPath()
920
921
922
923
924
	if err != nil {
		return err
	}
	err = os.Remove(fp)
	if err != nil {
925
		slog.Info(fmt.Sprintf("couldn't remove manifest file '%s': %v", fp, err))
926
927
928
929
930
931
		return err
	}

	return nil
}

Patrick Devine's avatar
Patrick Devine committed
932
func ShowModelfile(model *Model) (string, error) {
Michael Yang's avatar
Michael Yang committed
933
	var mt struct {
Patrick Devine's avatar
Patrick Devine committed
934
		*Model
Michael Yang's avatar
Michael Yang committed
935
		From       string
Michael Yang's avatar
Michael Yang committed
936
		Parameters map[string][]any
Patrick Devine's avatar
Patrick Devine committed
937
938
	}

Michael Yang's avatar
Michael Yang committed
939
	mt.Parameters = make(map[string][]any)
Patrick Devine's avatar
Patrick Devine committed
940
	for k, v := range model.Options {
Michael Yang's avatar
Michael Yang committed
941
942
943
		if s, ok := v.([]any); ok {
			mt.Parameters[k] = s
			continue
Patrick Devine's avatar
Patrick Devine committed
944
945
		}

Michael Yang's avatar
Michael Yang committed
946
		mt.Parameters[k] = []any{v}
Patrick Devine's avatar
Patrick Devine committed
947
948
	}

Michael Yang's avatar
Michael Yang committed
949
950
	mt.Model = model
	mt.From = model.ModelPath
Patrick Devine's avatar
Patrick Devine committed
951

952
953
	if model.ParentModel != "" {
		mt.From = model.ParentModel
Patrick Devine's avatar
Patrick Devine committed
954
955
956
957
958
959
960
961
	}

	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 }}"""
962
963

{{- if .System }}
Patrick Devine's avatar
Patrick Devine committed
964
SYSTEM """{{ .System }}"""
965
{{- end }}
966
967
968
969

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

Michael Yang's avatar
Michael Yang committed
971
972
973
974
{{- range $k, $v := .Parameters }}
{{- range $parameter := $v }}
PARAMETER {{ $k }} {{ printf "%#v" $parameter }}
{{- end }}
Michael Yang's avatar
Michael Yang committed
975
{{- end }}`
Patrick Devine's avatar
Patrick Devine committed
976
977
978

	tmpl, err := template.New("").Parse(modelFile)
	if err != nil {
979
		slog.Info(fmt.Sprintf("error parsing template: %q", err))
Patrick Devine's avatar
Patrick Devine committed
980
981
982
983
984
985
		return "", err
	}

	var buf bytes.Buffer

	if err = tmpl.Execute(&buf, mt); err != nil {
986
		slog.Info(fmt.Sprintf("error executing template: %q", err))
Patrick Devine's avatar
Patrick Devine committed
987
988
989
990
991
992
		return "", err
	}

	return buf.String(), nil
}

993
func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
994
	mp := ParseModelPath(name)
995
996
	fn(api.ProgressResponse{Status: "retrieving manifest"})

997
998
999
1000
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
1001
	manifest, _, err := GetManifest(mp)
1002
	if err != nil {
1003
		fn(api.ProgressResponse{Status: "couldn't retrieve manifest"})
1004
1005
1006
1007
		return err
	}

	var layers []*Layer
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1008
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
1009
	layers = append(layers, manifest.Config)
1010
1011

	for _, layer := range layers {
Michael Yang's avatar
Michael Yang committed
1012
		if err := uploadBlob(ctx, mp, layer, regOpts, fn); err != nil {
1013
			slog.Info(fmt.Sprintf("error uploading blob: %v", err))
1014
1015
1016
			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())
			}
1017
1018
			return err
		}
1019
1020
	}

1021
	fn(api.ProgressResponse{Status: "pushing manifest"})
Michael Yang's avatar
Michael Yang committed
1022
1023
	requestURL := mp.BaseURL()
	requestURL = requestURL.JoinPath("v2", mp.GetNamespaceRepository(), "manifests", mp.Tag)
1024
1025
1026
1027
1028
1029

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

Michael Yang's avatar
Michael Yang committed
1030
1031
	headers := make(http.Header)
	headers.Set("Content-Type", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1032
	resp, err := makeRequestWithRetry(ctx, http.MethodPut, requestURL, headers, bytes.NewReader(manifestJSON), regOpts)
1033
1034
1035
1036
1037
	if err != nil {
		return err
	}
	defer resp.Body.Close()

1038
	fn(api.ProgressResponse{Status: "success"})
1039
1040
1041
1042

	return nil
}

1043
func PullModel(ctx context.Context, name string, regOpts *RegistryOptions, fn func(api.ProgressResponse)) error {
1044
1045
	mp := ParseModelPath(name)

1046
1047
1048
1049
1050
	var manifest *ManifestV2
	var err error
	var noprune string

	// build deleteMap to prune unused layers
Michael Yang's avatar
Michael Yang committed
1051
	deleteMap := make(map[string]struct{})
1052
1053
1054
1055
1056
1057
1058
1059
1060

	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
1061
				deleteMap[l.Digest] = struct{}{}
1062
			}
Michael Yang's avatar
Michael Yang committed
1063
			deleteMap[manifest.Config.Digest] = struct{}{}
1064
1065
1066
		}
	}

1067
1068
	if mp.ProtocolScheme == "http" && !regOpts.Insecure {
		return fmt.Errorf("insecure protocol http")
1069
	}
1070

1071
	fn(api.ProgressResponse{Status: "pulling manifest"})
1072

1073
	manifest, err = pullModelManifest(ctx, mp, regOpts)
1074
	if err != nil {
1075
		return fmt.Errorf("pull model manifest: %s", err)
1076
1077
1078
	}

	var layers []*Layer
Bruce MacDonald's avatar
Bruce MacDonald committed
1079
	layers = append(layers, manifest.Layers...)
Michael Yang's avatar
Michael Yang committed
1080
	layers = append(layers, manifest.Config)
1081
1082

	for _, layer := range layers {
1083
1084
1085
1086
1087
1088
1089
1090
		if err := downloadBlob(
			ctx,
			downloadOpts{
				mp:      mp,
				digest:  layer.Digest,
				regOpts: regOpts,
				fn:      fn,
			}); err != nil {
1091
1092
			return err
		}
1093
		delete(deleteMap, layer.Digest)
1094
	}
1095
	delete(deleteMap, manifest.Config.Digest)
1096

Michael Yang's avatar
Michael Yang committed
1097
1098
1099
	fn(api.ProgressResponse{Status: "verifying sha256 digest"})
	for _, layer := range layers {
		if err := verifyBlob(layer.Digest); err != nil {
1100
1101
1102
1103
1104
1105
1106
1107
			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
1108
					slog.Info(fmt.Sprintf("couldn't remove file with digest mismatch '%s': %v", fp, err))
1109
1110
				}
			}
Michael Yang's avatar
Michael Yang committed
1111
1112
1113
1114
			return err
		}
	}

1115
	fn(api.ProgressResponse{Status: "writing manifest"})
1116

1117
	manifestJSON, err := json.Marshal(manifest)
1118
1119
1120
1121
	if err != nil {
		return err
	}

1122
	fp, err := mp.GetManifestPath()
1123
1124
1125
	if err != nil {
		return err
	}
1126
1127
1128
	if err := os.MkdirAll(filepath.Dir(fp), 0o755); err != nil {
		return err
	}
1129

Bruce MacDonald's avatar
Bruce MacDonald committed
1130
	err = os.WriteFile(fp, manifestJSON, 0o644)
1131
	if err != nil {
1132
		slog.Info(fmt.Sprintf("couldn't write to %s", fp))
1133
1134
1135
		return err
	}

1136
1137
1138
1139
1140
1141
1142
1143
	if noprune == "" {
		fn(api.ProgressResponse{Status: "removing any unused layers"})
		err = deleteUnusedLayers(nil, deleteMap, false)
		if err != nil {
			return err
		}
	}

1144
	fn(api.ProgressResponse{Status: "success"})
1145
1146
1147
1148

	return nil
}

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

Michael Yang's avatar
Michael Yang committed
1152
1153
	headers := make(http.Header)
	headers.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
Michael Yang's avatar
Michael Yang committed
1154
	resp, err := makeRequestWithRetry(ctx, http.MethodGet, requestURL, headers, nil, regOpts)
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
	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
1169
func GetSHA256Digest(r io.Reader) (string, int64) {
Michael Yang's avatar
Michael Yang committed
1170
1171
1172
1173
1174
1175
	h := sha256.New()
	n, err := io.Copy(h, r)
	if err != nil {
		log.Fatal(err)
	}

Michael Yang's avatar
Michael Yang committed
1176
	return fmt.Sprintf("sha256:%x", h.Sum(nil)), n
1177
1178
}

1179
1180
var errUnauthorized = fmt.Errorf("unauthorized")

Michael Yang's avatar
Michael Yang committed
1181
func makeRequestWithRetry(ctx context.Context, method string, requestURL *url.URL, headers http.Header, body io.ReadSeeker, regOpts *RegistryOptions) (*http.Response, error) {
Michael Yang's avatar
Michael Yang committed
1182
1183
	for i := 0; i < 2; i++ {
		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
1195
1196
1197

		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
1198
1199
1200
			if err != nil {
				return nil, err
			}
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
		}
	}

Michael Yang's avatar
Michael Yang committed
1221
	return nil, errUnauthorized
Michael Yang's avatar
Michael Yang committed
1222
1223
}

Michael Yang's avatar
Michael Yang committed
1224
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
1225
	if requestURL.Scheme != "http" && regOpts != nil && regOpts.Insecure {
Michael Yang's avatar
Michael Yang committed
1226
		requestURL.Scheme = "http"
1227
1228
	}

Michael Yang's avatar
Michael Yang committed
1229
	req, err := http.NewRequestWithContext(ctx, method, requestURL.String(), body)
1230
1231
1232
1233
	if err != nil {
		return nil, err
	}

Michael Yang's avatar
Michael Yang committed
1234
1235
1236
1237
	if headers != nil {
		req.Header = headers
	}

Michael Yang's avatar
Michael Yang committed
1238
1239
1240
1241
1242
1243
	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)
		}
1244
1245
	}

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

Michael Yang's avatar
Michael Yang committed
1248
1249
1250
1251
1252
1253
1254
1255
1256
	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
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
	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)
1269
1270
1271
1272
1273
1274
	if err != nil {
		return nil, err
	}

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

Patrick Devine's avatar
Patrick Devine committed
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
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"),
	}
}

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

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

	return nil
}