cmd.go 19.4 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
3
package cmd

import (
Michael Yang's avatar
Michael Yang committed
4
	"bufio"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
5
	"context"
6
7
8
	"crypto/ed25519"
	"crypto/rand"
	"encoding/pem"
Michael Yang's avatar
Michael Yang committed
9
	"errors"
Bruce MacDonald's avatar
Bruce MacDonald committed
10
	"fmt"
Michael Yang's avatar
Michael Yang committed
11
	"io"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
12
13
	"log"
	"net"
Michael Yang's avatar
Michael Yang committed
14
	"net/http"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
15
	"os"
16
	"os/exec"
17
	"path"
18
	"path/filepath"
19
	"runtime"
Michael Yang's avatar
Michael Yang committed
20
	"strings"
Michael Yang's avatar
Michael Yang committed
21
	"time"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
22

Michael Yang's avatar
Michael Yang committed
23
	"github.com/chzyer/readline"
Patrick Devine's avatar
Patrick Devine committed
24
25
	"github.com/dustin/go-humanize"
	"github.com/olekukonko/tablewriter"
Michael Yang's avatar
Michael Yang committed
26
	"github.com/spf13/cobra"
27
	"golang.org/x/crypto/ssh"
Michael Yang's avatar
Michael Yang committed
28

Jeffrey Morgan's avatar
Jeffrey Morgan committed
29
	"github.com/jmorganca/ollama/api"
Patrick Devine's avatar
Patrick Devine committed
30
	"github.com/jmorganca/ollama/format"
31
	"github.com/jmorganca/ollama/progressbar"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
32
	"github.com/jmorganca/ollama/server"
Michael Yang's avatar
Michael Yang committed
33
	"github.com/jmorganca/ollama/version"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
34
35
)

36
func CreateHandler(cmd *cobra.Command, args []string) error {
37
	filename, _ := cmd.Flags().GetString("file")
38
39
40
41
42
	filename, err := filepath.Abs(filename)
	if err != nil {
		return err
	}

43
44
45
46
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
47

Michael Yang's avatar
Michael Yang committed
48
49
	var spinner *Spinner

50
51
	var currentDigest string
	var bar *progressbar.ProgressBar
Michael Yang's avatar
Michael Yang committed
52

53
54
55
56
57
58
59
	request := api.CreateRequest{Name: args[0], Path: filename}
	fn := func(resp api.ProgressResponse) error {
		if resp.Digest != currentDigest && resp.Digest != "" {
			if spinner != nil {
				spinner.Stop()
			}
			currentDigest = resp.Digest
60
61
62
63
64
65
66
67
68
69
70
71
			switch {
			case strings.Contains(resp.Status, "embeddings"):
				bar = progressbar.Default(int64(resp.Total), resp.Status)
				bar.Set(resp.Completed)
			default:
				// pulling
				bar = progressbar.DefaultBytes(
					int64(resp.Total),
					resp.Status,
				)
				bar.Set(resp.Completed)
			}
72
73
74
75
76
77
78
79
80
81
		} else if resp.Digest == currentDigest && resp.Digest != "" {
			bar.Set(resp.Completed)
		} else {
			currentDigest = ""
			if spinner != nil {
				spinner.Stop()
			}
			spinner = NewSpinner(resp.Status)
			go spinner.Spin(100 * time.Millisecond)
		}
82

83
84
85
86
87
88
89
		return nil
	}

	if err := client.Create(context.Background(), &request, fn); err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
90
91
	if spinner != nil {
		spinner.Stop()
92
93
94
		if spinner.description != "success" {
			return errors.New("unexpected end to create model")
		}
Michael Yang's avatar
Michael Yang committed
95
96
	}

97
98
99
	return nil
}

100
func RunHandler(cmd *cobra.Command, args []string) error {
101
102
103
104
105
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

106
	mp := server.ParseModelPath(args[0])
107
108
109
110
	if err != nil {
		return err
	}

111
112
113
114
	if mp.ProtocolScheme == "http" && !insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
115
116
117
118
119
120
	fp, err := mp.GetManifestPath(false)
	if err != nil {
		return err
	}

	_, err = os.Stat(fp)
Michael Yang's avatar
Michael Yang committed
121
122
	switch {
	case errors.Is(err, os.ErrNotExist):
123
		if err := pull(args[0], insecure); err != nil {
Michael Yang's avatar
Michael Yang committed
124
125
126
127
128
129
130
131
			var apiStatusError api.StatusError
			if !errors.As(err, &apiStatusError) {
				return err
			}

			if apiStatusError.StatusCode != http.StatusBadGateway {
				return err
			}
Michael Yang's avatar
Michael Yang committed
132
133
		}
	case err != nil:
Michael Yang's avatar
Michael Yang committed
134
135
136
137
		return err
	}

	return RunGenerate(cmd, args)
Bruce MacDonald's avatar
Bruce MacDonald committed
138
139
}

140
func PushHandler(cmd *cobra.Command, args []string) error {
141
142
143
144
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
145

146
147
148
149
150
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

151
152
153
	var currentDigest string
	var bar *progressbar.ProgressBar

154
	request := api.PushRequest{Name: args[0], Insecure: insecure}
155
	fn := func(resp api.ProgressResponse) error {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
		if resp.Digest != currentDigest && resp.Digest != "" {
			currentDigest = resp.Digest
			bar = progressbar.DefaultBytes(
				int64(resp.Total),
				fmt.Sprintf("pushing %s...", resp.Digest[7:19]),
			)

			bar.Set(resp.Completed)
		} else if resp.Digest == currentDigest && resp.Digest != "" {
			bar.Set(resp.Completed)
		} else {
			currentDigest = ""
			fmt.Println(resp.Status)
		}
170
171
172
173
174
175
		return nil
	}

	if err := client.Push(context.Background(), &request, fn); err != nil {
		return err
	}
176
177
178
179
180

	if bar != nil && !bar.IsFinished() {
		return errors.New("unexpected end to push model")
	}

181
182
183
	return nil
}

184
func ListHandler(cmd *cobra.Command, args []string) error {
185
186
187
188
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
189
190
191
192
193
194
195
196
197

	models, err := client.List(context.Background())
	if err != nil {
		return err
	}

	var data [][]string

	for _, m := range models.Models {
Michael Yang's avatar
Michael Yang committed
198
		if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
Patrick Devine's avatar
Patrick Devine committed
199
			data = append(data, []string{m.Name, m.Digest[:12], humanize.Bytes(uint64(m.Size)), format.HumanTime(m.ModifiedAt, "Never")})
Michael Yang's avatar
Michael Yang committed
200
		}
Patrick Devine's avatar
Patrick Devine committed
201
202
203
	}

	table := tablewriter.NewWriter(os.Stdout)
Patrick Devine's avatar
Patrick Devine committed
204
	table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
Patrick Devine's avatar
Patrick Devine committed
205
206
207
208
209
210
211
212
213
214
215
216
	table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetHeaderLine(false)
	table.SetBorder(false)
	table.SetNoWhiteSpace(true)
	table.SetTablePadding("\t")
	table.AppendBulk(data)
	table.Render()

	return nil
}

217
func DeleteHandler(cmd *cobra.Command, args []string) error {
218
219
220
221
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
222

223
224
225
226
227
228
	for _, name := range args {
		req := api.DeleteRequest{Name: name}
		if err := client.Delete(context.Background(), &req); err != nil {
			return err
		}
		fmt.Printf("deleted '%s'\n", name)
229
230
231
232
	}
	return nil
}

Patrick Devine's avatar
Patrick Devine committed
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
func ShowHandler(cmd *cobra.Command, args []string) error {
	client, err := api.FromEnv()
	if err != nil {
		return err
	}

	if len(args) != 1 {
		return errors.New("missing model name")
	}

	license, errLicense := cmd.Flags().GetBool("license")
	modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
	parameters, errParams := cmd.Flags().GetBool("parameters")
	system, errSystem := cmd.Flags().GetBool("system")
	template, errTemplate := cmd.Flags().GetBool("template")

	for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
		if boolErr != nil {
			return errors.New("error retrieving flags")
		}
	}

	flagsSet := 0
	showType := ""

	if license {
		flagsSet++
		showType = "license"
	}

	if modelfile {
		flagsSet++
		showType = "modelfile"
	}

	if parameters {
		flagsSet++
		showType = "parameters"
	}

	if system {
		flagsSet++
		showType = "system"
	}

	if template {
		flagsSet++
		showType = "template"
	}

	if flagsSet > 1 {
284
		return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
Patrick Devine's avatar
Patrick Devine committed
285
	} else if flagsSet == 0 {
286
		return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
Patrick Devine's avatar
Patrick Devine committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
	}

	req := api.ShowRequest{Name: args[0]}
	resp, err := client.Show(context.Background(), &req)
	if err != nil {
		return err
	}

	switch showType {
	case "license":
		fmt.Println(resp.License)
	case "modelfile":
		fmt.Println(resp.Modelfile)
	case "parameters":
		fmt.Println(resp.Parameters)
	case "system":
		fmt.Println(resp.System)
	case "template":
		fmt.Println(resp.Template)
	}

	return nil
}

Patrick Devine's avatar
Patrick Devine committed
311
func CopyHandler(cmd *cobra.Command, args []string) error {
312
313
314
315
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
316
317
318
319
320
321
322
323
324

	req := api.CopyRequest{Source: args[0], Destination: args[1]}
	if err := client.Copy(context.Background(), &req); err != nil {
		return err
	}
	fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
	return nil
}

325
func PullHandler(cmd *cobra.Command, args []string) error {
326
327
328
329
330
331
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

	return pull(args[0], insecure)
332
333
}

334
func pull(model string, insecure bool) error {
335
336
337
338
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
339

340
	var currentDigest string
Bruce MacDonald's avatar
Bruce MacDonald committed
341
	var bar *progressbar.ProgressBar
Michael Yang's avatar
Michael Yang committed
342

343
	request := api.PullRequest{Name: model, Insecure: insecure}
344
345
346
	fn := func(resp api.ProgressResponse) error {
		if resp.Digest != currentDigest && resp.Digest != "" {
			currentDigest = resp.Digest
347
348
			bar = progressbar.DefaultBytes(
				int64(resp.Total),
349
				fmt.Sprintf("pulling %s...", resp.Digest[7:19]),
350
			)
351
352
353

			bar.Set(resp.Completed)
		} else if resp.Digest == currentDigest && resp.Digest != "" {
354
355
			bar.Set(resp.Completed)
		} else {
356
			currentDigest = ""
357
358
			fmt.Println(resp.Status)
		}
359

360
361
		return nil
	}
362

363
364
365
	if err := client.Pull(context.Background(), &request, fn); err != nil {
		return err
	}
366
367
368
369
370

	if bar != nil && !bar.IsFinished() {
		return errors.New("unexpected end to pull model")
	}

371
	return nil
Michael Yang's avatar
Michael Yang committed
372
373
}

374
func RunGenerate(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
375
	if len(args) > 1 {
Michael Yang's avatar
Michael Yang committed
376
		// join all args into a single prompt
377
		return generate(cmd, args[0], strings.Join(args[1:], " "))
Michael Yang's avatar
Michael Yang committed
378
379
	}

Michael Yang's avatar
Michael Yang committed
380
	if readline.IsTerminal(int(os.Stdin.Fd())) {
381
		return generateInteractive(cmd, args[0])
Michael Yang's avatar
Michael Yang committed
382
383
	}

384
	return generateBatch(cmd, args[0])
Michael Yang's avatar
Michael Yang committed
385
386
}

Michael Yang's avatar
Michael Yang committed
387
type generateContextKey string
Michael Yang's avatar
Michael Yang committed
388

389
func generate(cmd *cobra.Command, model, prompt string) error {
Michael Yang's avatar
Michael Yang committed
390
	if len(strings.TrimSpace(prompt)) > 0 {
391
392
393
394
		client, err := api.FromEnv()
		if err != nil {
			return err
		}
Michael Yang's avatar
Michael Yang committed
395

Michael Yang's avatar
Michael Yang committed
396
397
		spinner := NewSpinner("")
		go spinner.Spin(60 * time.Millisecond)
Michael Yang's avatar
Michael Yang committed
398

399
400
		var latest api.GenerateResponse

Michael Yang's avatar
Michael Yang committed
401
		generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
Michael Yang's avatar
Michael Yang committed
402
403
404
405
		if !ok {
			generateContext = []int{}
		}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
406
		request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
Michael Yang's avatar
Michael Yang committed
407
		fn := func(response api.GenerateResponse) error {
Michael Yang's avatar
Michael Yang committed
408
409
410
			if !spinner.IsFinished() {
				spinner.Finish()
			}
Michael Yang's avatar
Michael Yang committed
411

Michael Yang's avatar
Michael Yang committed
412
			latest = response
413

Michael Yang's avatar
Michael Yang committed
414
			fmt.Print(response.Response)
Michael Yang's avatar
Michael Yang committed
415
			return nil
416
417
418
		}

		if err := client.Generate(context.Background(), &request, fn); err != nil {
419
420
421
422
423
424
425
426
427
428
429
430
			if strings.Contains(err.Error(), "failed to load model") {
				// tell the user to check the server log, if it exists locally
				home, nestedErr := os.UserHomeDir()
				if nestedErr != nil {
					// return the original error
					return err
				}
				logPath := filepath.Join(home, ".ollama", "logs", "server.log")
				if _, nestedErr := os.Stat(logPath); nestedErr == nil {
					err = fmt.Errorf("%w\nFor more details, check the error logs at %s", err, logPath)
				}
			}
431
432
			return err
		}
Michael Yang's avatar
Michael Yang committed
433

Michael Yang's avatar
Michael Yang committed
434
435
		fmt.Println()
		fmt.Println()
436

437
438
439
440
		if !latest.Done {
			return errors.New("unexpected end of response")
		}

441
442
443
444
445
446
447
448
		verbose, err := cmd.Flags().GetBool("verbose")
		if err != nil {
			return err
		}

		if verbose {
			latest.Summary()
		}
Michael Yang's avatar
Michael Yang committed
449
450
451
452

		ctx := cmd.Context()
		ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
		cmd.SetContext(ctx)
Michael Yang's avatar
Michael Yang committed
453
	}
Michael Yang's avatar
Michael Yang committed
454
455
456
457

	return nil
}

458
func generateInteractive(cmd *cobra.Command, model string) error {
Michael Yang's avatar
Michael Yang committed
459
460
461
462
463
464
465
466
467
468
469
	home, err := os.UserHomeDir()
	if err != nil {
		return err
	}

	completer := readline.NewPrefixCompleter(
		readline.PcItem("/help"),
		readline.PcItem("/list"),
		readline.PcItem("/set",
			readline.PcItem("history"),
			readline.PcItem("nohistory"),
Michael Yang's avatar
Michael Yang committed
470
471
			readline.PcItem("verbose"),
			readline.PcItem("quiet"),
Michael Yang's avatar
Michael Yang committed
472
473
474
475
476
477
			readline.PcItem("mode",
				readline.PcItem("vim"),
				readline.PcItem("emacs"),
				readline.PcItem("default"),
			),
		),
478
479
		readline.PcItem("/show",
			readline.PcItem("license"),
Patrick Devine's avatar
Patrick Devine committed
480
481
			readline.PcItem("modelfile"),
			readline.PcItem("parameters"),
482
483
484
			readline.PcItem("system"),
			readline.PcItem("template"),
		),
Michael Yang's avatar
Michael Yang committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
		readline.PcItem("/exit"),
		readline.PcItem("/bye"),
	)

	usage := func() {
		fmt.Fprintln(os.Stderr, "commands:")
		fmt.Fprintln(os.Stderr, completer.Tree("  "))
	}

	config := readline.Config{
		Prompt:       ">>> ",
		HistoryFile:  filepath.Join(home, ".ollama", "history"),
		AutoComplete: completer,
	}

	scanner, err := readline.NewEx(&config)
	if err != nil {
		return err
	}
	defer scanner.Close()

506
507
508
	var multiLineBuffer string
	var isMultiLine bool

Michael Yang's avatar
Michael Yang committed
509
510
511
512
513
514
	for {
		line, err := scanner.Readline()
		switch {
		case errors.Is(err, io.EOF):
			return nil
		case errors.Is(err, readline.ErrInterrupt):
515
516
517
518
			if line == "" {
				return nil
			}

Michael Yang's avatar
Michael Yang committed
519
520
			continue
		case err != nil:
Michael Yang's avatar
Michael Yang committed
521
522
523
			return err
		}

Michael Yang's avatar
Michael Yang committed
524
		line = strings.TrimSpace(line)
Michael Yang's avatar
Michael Yang committed
525

Michael Yang's avatar
Michael Yang committed
526
		switch {
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
		case isMultiLine:
			if strings.HasSuffix(line, `"""`) {
				isMultiLine = false
				multiLineBuffer += strings.TrimSuffix(line, `"""`)
				line = multiLineBuffer
				multiLineBuffer = ""
				scanner.SetPrompt(">>> ")
			} else {
				multiLineBuffer += line + " "
				continue
			}
		case strings.HasPrefix(line, `"""`):
			isMultiLine = true
			multiLineBuffer = strings.TrimPrefix(line, `"""`) + " "
			scanner.SetPrompt("... ")
			continue
Michael Yang's avatar
Michael Yang committed
543
544
		case strings.HasPrefix(line, "/list"):
			args := strings.Fields(line)
545
			if err := ListHandler(cmd, args[1:]); err != nil {
Michael Yang's avatar
Michael Yang committed
546
547
548
549
550
551
552
553
554
555
556
557
558
559
				return err
			}

			continue
		case strings.HasPrefix(line, "/set"):
			args := strings.Fields(line)
			if len(args) > 1 {
				switch args[1] {
				case "history":
					scanner.HistoryEnable()
					continue
				case "nohistory":
					scanner.HistoryDisable()
					continue
Michael Yang's avatar
Michael Yang committed
560
561
562
563
564
565
				case "verbose":
					cmd.Flags().Set("verbose", "true")
					continue
				case "quiet":
					cmd.Flags().Set("verbose", "false")
					continue
Michael Yang's avatar
Michael Yang committed
566
567
568
569
570
571
572
573
574
				case "mode":
					if len(args) > 2 {
						switch args[2] {
						case "vim":
							scanner.SetVimMode(true)
							continue
						case "emacs", "default":
							scanner.SetVimMode(false)
							continue
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
						default:
							usage()
							continue
						}
					} else {
						usage()
						continue
					}
				}
			} else {
				usage()
				continue
			}
		case strings.HasPrefix(line, "/show"):
			args := strings.Fields(line)
			if len(args) > 1 {
Patrick Devine's avatar
Patrick Devine committed
591
				resp, err := server.GetModelInfo(model)
592
				if err != nil {
Patrick Devine's avatar
Patrick Devine committed
593
					fmt.Println("error: couldn't get model")
594
595
					continue
				}
Patrick Devine's avatar
Patrick Devine committed
596

597
598
				switch args[1] {
				case "license":
Patrick Devine's avatar
Patrick Devine committed
599
600
601
602
603
					fmt.Println(resp.License)
				case "modelfile":
					fmt.Println(resp.Modelfile)
				case "parameters":
					fmt.Println(resp.Parameters)
604
				case "system":
Patrick Devine's avatar
Patrick Devine committed
605
					fmt.Println(resp.System)
606
				case "template":
Patrick Devine's avatar
Patrick Devine committed
607
					fmt.Println(resp.Template)
608
				default:
Patrick Devine's avatar
Patrick Devine committed
609
					fmt.Println("error: unknown command")
Michael Yang's avatar
Michael Yang committed
610
				}
Patrick Devine's avatar
Patrick Devine committed
611
612

				continue
613
614
615
			} else {
				usage()
				continue
Michael Yang's avatar
Michael Yang committed
616
617
618
619
620
621
622
623
624
625
626
627
			}
		case line == "/help", line == "/?":
			usage()
			continue
		case line == "/exit", line == "/bye":
			return nil
		}

		if err := generate(cmd, model, line); err != nil {
			return err
		}
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
628
629
}

630
func generateBatch(cmd *cobra.Command, model string) error {
Michael Yang's avatar
Michael Yang committed
631
632
633
634
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		prompt := scanner.Text()
		fmt.Printf(">>> %s\n", prompt)
635
		if err := generate(cmd, model, prompt); err != nil {
Michael Yang's avatar
Michael Yang committed
636
637
638
639
640
641
642
			return err
		}
	}

	return nil
}

643
func RunServer(cmd *cobra.Command, _ []string) error {
644
	host, port := "127.0.0.1", "11434"
645
646
647
648

	parts := strings.Split(os.Getenv("OLLAMA_HOST"), ":")
	if ip := net.ParseIP(parts[0]); ip != nil {
		host = ip.String()
649
650
	}

651
652
653
654
655
656
657
	if len(parts) > 1 {
		port = parts[1]
	}

	// deprecated: include port in OLLAMA_HOST
	if p := os.Getenv("OLLAMA_PORT"); p != "" {
		port = p
Jeffrey Morgan's avatar
Jeffrey Morgan committed
658
	}
659

660
661
662
663
664
	err := initializeKeypair()
	if err != nil {
		return err
	}

665
	ln, err := net.Listen("tcp", fmt.Sprintf("%s:%s", host, port))
666
667
668
	if err != nil {
		return err
	}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
669

670
671
672
673
674
	var origins []string
	if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
		origins = strings.Split(o, ",")
	}

675
676
677
678
679
680
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := server.PruneLayers(); err != nil {
			return err
		}
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
681
	return server.Serve(ln, origins)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
682
683
}

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
func initializeKeypair() error {
	home, err := os.UserHomeDir()
	if err != nil {
		return err
	}

	privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
	pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")

	_, err = os.Stat(privKeyPath)
	if os.IsNotExist(err) {
		fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
		_, privKey, err := ed25519.GenerateKey(rand.Reader)
		if err != nil {
			return err
		}

		privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
		if err != nil {
			return err
		}

706
707
708
709
710
		err = os.MkdirAll(path.Dir(privKeyPath), 0o700)
		if err != nil {
			return fmt.Errorf("could not create directory %w", err)
		}

711
		err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
712
713
714
715
716
717
718
719
720
721
722
		if err != nil {
			return err
		}

		sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
		if err != nil {
			return err
		}

		pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())

723
		err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
724
725
726
727
728
729
730
731
732
		if err != nil {
			return err
		}

		fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
	}
	return nil
}

Bruce MacDonald's avatar
Bruce MacDonald committed
733
func startMacApp(client *api.Client) error {
Bruce MacDonald's avatar
Bruce MacDonald committed
734
735
736
737
738
	exe, err := os.Executable()
	if err != nil {
		return err
	}
	link, err := os.Readlink(exe)
Bruce MacDonald's avatar
Bruce MacDonald committed
739
740
741
	if err != nil {
		return err
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
742
743
744
	if !strings.Contains(link, "Ollama.app") {
		return fmt.Errorf("could not find ollama app")
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
	path := strings.Split(link, "Ollama.app")
	if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
		return err
	}
	// wait for the server to start
	timeout := time.After(5 * time.Second)
	tick := time.Tick(500 * time.Millisecond)
	for {
		select {
		case <-timeout:
			return errors.New("timed out waiting for server to start")
		case <-tick:
			if err := client.Heartbeat(context.Background()); err == nil {
				return nil // server has started
			}
		}
	}
}

764
func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
765
766
767
768
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
769
	if err := client.Heartbeat(context.Background()); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
770
771
772
773
774
		if !strings.Contains(err.Error(), "connection refused") {
			return err
		}
		if runtime.GOOS == "darwin" {
			if err := startMacApp(client); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
775
				return fmt.Errorf("could not connect to ollama app, is it running?")
776
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
777
		} else {
778
779
780
781
782
783
			return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
		}
	}
	return nil
}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
784
785
786
787
func NewCLI() *cobra.Command {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	rootCmd := &cobra.Command{
788
789
790
791
		Use:           "ollama",
		Short:         "Large language model runner",
		SilenceUsage:  true,
		SilenceErrors: true,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
792
793
794
		CompletionOptions: cobra.CompletionOptions{
			DisableDefaultCmd: true,
		},
Michael Yang's avatar
Michael Yang committed
795
		Version: version.Version,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
796
797
798
799
	}

	cobra.EnableCommandSorting = false

800
	createCmd := &cobra.Command{
801
802
803
804
805
		Use:     "create MODEL",
		Short:   "Create a model from a Modelfile",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    CreateHandler,
806
807
808
809
	}

	createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")

Patrick Devine's avatar
Patrick Devine committed
810
811
812
813
814
815
816
817
818
819
820
821
822
823
	showCmd := &cobra.Command{
		Use:     "show MODEL",
		Short:   "Show information for a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    ShowHandler,
	}

	showCmd.Flags().Bool("license", false, "Show license of a model")
	showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
	showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
	showCmd.Flags().Bool("template", false, "Show template of a model")
	showCmd.Flags().Bool("system", false, "Show system prompt of a model")

Jeffrey Morgan's avatar
Jeffrey Morgan committed
824
	runCmd := &cobra.Command{
825
826
827
828
829
		Use:     "run MODEL [PROMPT]",
		Short:   "Run a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    RunHandler,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
830
831
	}

832
	runCmd.Flags().Bool("verbose", false, "Show timings for response")
833
	runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
834

Jeffrey Morgan's avatar
Jeffrey Morgan committed
835
836
837
838
	serveCmd := &cobra.Command{
		Use:     "serve",
		Aliases: []string{"start"},
		Short:   "Start ollama",
Michael Yang's avatar
Michael Yang committed
839
		RunE:    RunServer,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
840
841
	}

842
	pullCmd := &cobra.Command{
843
844
845
846
847
		Use:     "pull MODEL",
		Short:   "Pull a model from a registry",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    PullHandler,
848
849
	}

850
851
	pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")

852
	pushCmd := &cobra.Command{
853
854
855
856
857
		Use:     "push MODEL",
		Short:   "Push a model to a registry",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    PushHandler,
858
859
	}

860
861
	pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")

Patrick Devine's avatar
Patrick Devine committed
862
	listCmd := &cobra.Command{
863
		Use:     "list",
Patrick Devine's avatar
Patrick Devine committed
864
		Aliases: []string{"ls"},
865
		Short:   "List models",
866
		PreRunE: checkServerHeartbeat,
867
		RunE:    ListHandler,
868
869
	}

Patrick Devine's avatar
Patrick Devine committed
870
	copyCmd := &cobra.Command{
871
872
873
874
875
		Use:     "cp",
		Short:   "Copy a model",
		Args:    cobra.MinimumNArgs(2),
		PreRunE: checkServerHeartbeat,
		RunE:    CopyHandler,
Patrick Devine's avatar
Patrick Devine committed
876
877
	}

878
	deleteCmd := &cobra.Command{
879
880
881
882
883
		Use:     "rm",
		Short:   "Remove a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    DeleteHandler,
Patrick Devine's avatar
Patrick Devine committed
884
885
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
886
887
	rootCmd.AddCommand(
		serveCmd,
888
		createCmd,
Patrick Devine's avatar
Patrick Devine committed
889
		showCmd,
890
		runCmd,
891
892
		pullCmd,
		pushCmd,
Patrick Devine's avatar
Patrick Devine committed
893
		listCmd,
Patrick Devine's avatar
Patrick Devine committed
894
		copyCmd,
895
		deleteCmd,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
896
897
898
899
	)

	return rootCmd
}