"test/test_image.py" did not exist on "9d9e716ff23a08051df3ca713b30f8ddc89e4e1c"
cmd.go 19.8 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/filepath"
18
	"runtime"
Michael Yang's avatar
Michael Yang committed
19
	"strings"
Michael Yang's avatar
Michael Yang committed
20
	"time"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
21

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

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

Patrick Devine's avatar
Patrick Devine committed
35
36
37
38
39
40
41
42
43
44
45
type Painter struct{}

func (p Painter) Paint(line []rune, l int) []rune {
	termType := os.Getenv("TERM")
	if termType == "xterm-256color" && len(line) == 0 {
		prompt := "Send a message (/? for help)"
		return []rune(fmt.Sprintf("\033[38;5;245m%s\033[%dD\033[0m", prompt, len(prompt)))
	}
	return line
}

46
func CreateHandler(cmd *cobra.Command, args []string) error {
47
	filename, _ := cmd.Flags().GetString("file")
48
49
50
51
52
	filename, err := filepath.Abs(filename)
	if err != nil {
		return err
	}

53
54
55
56
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
57

Michael Yang's avatar
Michael Yang committed
58
59
	var spinner *Spinner

60
61
	var currentDigest string
	var bar *progressbar.ProgressBar
Michael Yang's avatar
Michael Yang committed
62

63
64
65
66
67
68
69
	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
70
71
72
73
74
75
76
77
78
79
80
81
			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)
			}
82
83
84
85
86
87
88
89
90
91
		} 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)
		}
92

93
94
95
96
97
98
99
		return nil
	}

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

Michael Yang's avatar
Michael Yang committed
100
101
	if spinner != nil {
		spinner.Stop()
102
103
104
		if spinner.description != "success" {
			return errors.New("unexpected end to create model")
		}
Michael Yang's avatar
Michael Yang committed
105
106
	}

107
108
109
	return nil
}

110
func RunHandler(cmd *cobra.Command, args []string) error {
111
112
113
114
115
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

116
117
118
119
120
	mp := server.ParseModelPath(args[0])
	if mp.ProtocolScheme == "http" && !insecure {
		return fmt.Errorf("insecure protocol http")
	}

Patrick Devine's avatar
Patrick Devine committed
121
122
123
124
125
126
	fp, err := mp.GetManifestPath(false)
	if err != nil {
		return err
	}

	_, err = os.Stat(fp)
Michael Yang's avatar
Michael Yang committed
127
128
	switch {
	case errors.Is(err, os.ErrNotExist):
129
		if err := pull(args[0], insecure); err != nil {
Michael Yang's avatar
Michael Yang committed
130
131
132
133
134
135
136
137
			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
138
139
		}
	case err != nil:
Michael Yang's avatar
Michael Yang committed
140
141
142
143
		return err
	}

	return RunGenerate(cmd, args)
Bruce MacDonald's avatar
Bruce MacDonald committed
144
145
}

146
func PushHandler(cmd *cobra.Command, args []string) error {
147
148
149
150
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
151

152
153
154
155
156
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

157
158
159
	var currentDigest string
	var bar *progressbar.ProgressBar

160
	request := api.PushRequest{Name: args[0], Insecure: insecure}
161
	fn := func(resp api.ProgressResponse) error {
162
163
164
165
166
167
168
169
170
171
172
173
174
175
		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)
		}
176
177
178
179
180
181
		return nil
	}

	if err := client.Push(context.Background(), &request, fn); err != nil {
		return err
	}
182
183
184
185
186

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

187
188
189
	return nil
}

190
func ListHandler(cmd *cobra.Command, args []string) error {
191
192
193
194
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
195
196
197
198
199
200
201
202
203

	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
204
		if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
Patrick Devine's avatar
Patrick Devine committed
205
			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
206
		}
Patrick Devine's avatar
Patrick Devine committed
207
208
209
	}

	table := tablewriter.NewWriter(os.Stdout)
Patrick Devine's avatar
Patrick Devine committed
210
	table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
Patrick Devine's avatar
Patrick Devine committed
211
212
213
214
215
216
217
218
219
220
221
222
	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
}

223
func DeleteHandler(cmd *cobra.Command, args []string) error {
224
225
226
227
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
228

229
230
231
232
233
234
	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)
235
236
237
238
	}
	return nil
}

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

	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
317
func CopyHandler(cmd *cobra.Command, args []string) error {
318
319
320
321
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
322
323
324
325
326
327
328
329
330

	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
}

331
func PullHandler(cmd *cobra.Command, args []string) error {
332
333
334
335
336
337
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

	return pull(args[0], insecure)
338
339
}

340
func pull(model string, insecure bool) error {
341
342
343
344
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
345

346
	var currentDigest string
Bruce MacDonald's avatar
Bruce MacDonald committed
347
	var bar *progressbar.ProgressBar
Michael Yang's avatar
Michael Yang committed
348

349
	request := api.PullRequest{Name: model, Insecure: insecure}
350
351
352
	fn := func(resp api.ProgressResponse) error {
		if resp.Digest != currentDigest && resp.Digest != "" {
			currentDigest = resp.Digest
353
354
			bar = progressbar.DefaultBytes(
				int64(resp.Total),
355
				fmt.Sprintf("pulling %s...", resp.Digest[7:19]),
356
			)
357
358
359

			bar.Set(resp.Completed)
		} else if resp.Digest == currentDigest && resp.Digest != "" {
360
361
			bar.Set(resp.Completed)
		} else {
362
			currentDigest = ""
363
364
			fmt.Println(resp.Status)
		}
365

366
367
		return nil
	}
368

369
370
371
	if err := client.Pull(context.Background(), &request, fn); err != nil {
		return err
	}
372
373
374
375
376

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

377
	return nil
Michael Yang's avatar
Michael Yang committed
378
379
}

380
func RunGenerate(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
381
	if len(args) > 1 {
Michael Yang's avatar
Michael Yang committed
382
		// join all args into a single prompt
383
		return generate(cmd, args[0], strings.Join(args[1:], " "))
Michael Yang's avatar
Michael Yang committed
384
385
	}

Michael Yang's avatar
Michael Yang committed
386
	if readline.IsTerminal(int(os.Stdin.Fd())) {
387
		return generateInteractive(cmd, args[0])
Michael Yang's avatar
Michael Yang committed
388
389
	}

390
	return generateBatch(cmd, args[0])
Michael Yang's avatar
Michael Yang committed
391
392
}

Michael Yang's avatar
Michael Yang committed
393
type generateContextKey string
Michael Yang's avatar
Michael Yang committed
394

395
func generate(cmd *cobra.Command, model, prompt string) error {
Patrick Devine's avatar
Patrick Devine committed
396
397
398
399
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
400

Patrick Devine's avatar
Patrick Devine committed
401
402
	spinner := NewSpinner("")
	go spinner.Spin(60 * time.Millisecond)
Michael Yang's avatar
Michael Yang committed
403

Patrick Devine's avatar
Patrick Devine committed
404
	var latest api.GenerateResponse
405

Patrick Devine's avatar
Patrick Devine committed
406
407
408
409
	generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
	if !ok {
		generateContext = []int{}
	}
Michael Yang's avatar
Michael Yang committed
410

Patrick Devine's avatar
Patrick Devine committed
411
412
413
414
415
	request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
	fn := func(response api.GenerateResponse) error {
		if !spinner.IsFinished() {
			spinner.Finish()
		}
Michael Yang's avatar
Michael Yang committed
416

Patrick Devine's avatar
Patrick Devine committed
417
		latest = response
418

Patrick Devine's avatar
Patrick Devine committed
419
420
421
		fmt.Print(response.Response)
		return nil
	}
422

Patrick Devine's avatar
Patrick Devine committed
423
424
425
426
427
428
429
430
431
432
433
	if err := client.Generate(context.Background(), &request, fn); err != nil {
		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)
434
			}
435
		}
Patrick Devine's avatar
Patrick Devine committed
436
437
		return err
	}
Michael Yang's avatar
Michael Yang committed
438

Patrick Devine's avatar
Patrick Devine committed
439
	if prompt != "" {
Michael Yang's avatar
Michael Yang committed
440
441
		fmt.Println()
		fmt.Println()
Patrick Devine's avatar
Patrick Devine committed
442
	}
443

Patrick Devine's avatar
Patrick Devine committed
444
445
446
	if !latest.Done {
		return errors.New("unexpected end of response")
	}
447

Patrick Devine's avatar
Patrick Devine committed
448
449
450
451
	verbose, err := cmd.Flags().GetBool("verbose")
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
452

Patrick Devine's avatar
Patrick Devine committed
453
454
	if verbose {
		latest.Summary()
Michael Yang's avatar
Michael Yang committed
455
	}
Michael Yang's avatar
Michael Yang committed
456

Patrick Devine's avatar
Patrick Devine committed
457
458
459
460
	ctx := cmd.Context()
	ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
	cmd.SetContext(ctx)

Michael Yang's avatar
Michael Yang committed
461
462
463
	return nil
}

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

Patrick Devine's avatar
Patrick Devine committed
470
471
472
473
474
	// load the model
	if err := generate(cmd, model, ""); err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
475
476
477
478
479
480
	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
481
482
			readline.PcItem("verbose"),
			readline.PcItem("quiet"),
Michael Yang's avatar
Michael Yang committed
483
484
485
486
487
488
			readline.PcItem("mode",
				readline.PcItem("vim"),
				readline.PcItem("emacs"),
				readline.PcItem("default"),
			),
		),
489
490
		readline.PcItem("/show",
			readline.PcItem("license"),
Patrick Devine's avatar
Patrick Devine committed
491
492
			readline.PcItem("modelfile"),
			readline.PcItem("parameters"),
493
494
495
			readline.PcItem("system"),
			readline.PcItem("template"),
		),
Michael Yang's avatar
Michael Yang committed
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{
Patrick Devine's avatar
Patrick Devine committed
506
		Painter:      Painter{},
Michael Yang's avatar
Michael Yang committed
507
508
509
510
511
512
513
514
515
516
517
		Prompt:       ">>> ",
		HistoryFile:  filepath.Join(home, ".ollama", "history"),
		AutoComplete: completer,
	}

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

518
519
520
	var multiLineBuffer string
	var isMultiLine bool

Michael Yang's avatar
Michael Yang committed
521
522
523
524
525
526
	for {
		line, err := scanner.Readline()
		switch {
		case errors.Is(err, io.EOF):
			return nil
		case errors.Is(err, readline.ErrInterrupt):
527
528
529
530
			if line == "" {
				return nil
			}

Michael Yang's avatar
Michael Yang committed
531
532
			continue
		case err != nil:
Michael Yang's avatar
Michael Yang committed
533
534
535
			return err
		}

Michael Yang's avatar
Michael Yang committed
536
		line = strings.TrimSpace(line)
Michael Yang's avatar
Michael Yang committed
537

Michael Yang's avatar
Michael Yang committed
538
		switch {
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
		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
555
556
		case strings.HasPrefix(line, "/list"):
			args := strings.Fields(line)
557
			if err := ListHandler(cmd, args[1:]); err != nil {
Michael Yang's avatar
Michael Yang committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
				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
572
573
574
575
576
577
				case "verbose":
					cmd.Flags().Set("verbose", "true")
					continue
				case "quiet":
					cmd.Flags().Set("verbose", "false")
					continue
Michael Yang's avatar
Michael Yang committed
578
579
580
581
582
583
584
585
586
				case "mode":
					if len(args) > 2 {
						switch args[2] {
						case "vim":
							scanner.SetVimMode(true)
							continue
						case "emacs", "default":
							scanner.SetVimMode(false)
							continue
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
						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
603
				resp, err := server.GetModelInfo(model)
604
				if err != nil {
Patrick Devine's avatar
Patrick Devine committed
605
					fmt.Println("error: couldn't get model")
606
607
					continue
				}
Patrick Devine's avatar
Patrick Devine committed
608

609
610
				switch args[1] {
				case "license":
Patrick Devine's avatar
Patrick Devine committed
611
612
613
614
615
					fmt.Println(resp.License)
				case "modelfile":
					fmt.Println(resp.Modelfile)
				case "parameters":
					fmt.Println(resp.Parameters)
616
				case "system":
Patrick Devine's avatar
Patrick Devine committed
617
					fmt.Println(resp.System)
618
				case "template":
Patrick Devine's avatar
Patrick Devine committed
619
					fmt.Println(resp.Template)
620
				default:
Patrick Devine's avatar
Patrick Devine committed
621
					fmt.Println("error: unknown command")
Michael Yang's avatar
Michael Yang committed
622
				}
Patrick Devine's avatar
Patrick Devine committed
623
624

				continue
625
626
627
			} else {
				usage()
				continue
Michael Yang's avatar
Michael Yang committed
628
629
630
631
632
633
634
635
			}
		case line == "/help", line == "/?":
			usage()
			continue
		case line == "/exit", line == "/bye":
			return nil
		}

Patrick Devine's avatar
Patrick Devine committed
636
637
638
639
		if len(line) > 0 && line[0] != '/' {
			if err := generate(cmd, model, line); err != nil {
				return err
			}
Michael Yang's avatar
Michael Yang committed
640
641
		}
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
642
643
}

644
func generateBatch(cmd *cobra.Command, model string) error {
Michael Yang's avatar
Michael Yang committed
645
646
647
648
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		prompt := scanner.Text()
		fmt.Printf(">>> %s\n", prompt)
649
		if err := generate(cmd, model, prompt); err != nil {
Michael Yang's avatar
Michael Yang committed
650
651
652
653
654
655
656
			return err
		}
	}

	return nil
}

657
func RunServer(cmd *cobra.Command, _ []string) error {
658
	host, port := "127.0.0.1", "11434"
659
660
661
662

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

665
666
667
668
669
670
671
	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
672
	}
673

674
675
676
677
678
	err := initializeKeypair()
	if err != nil {
		return err
	}

679
	ln, err := net.Listen("tcp", fmt.Sprintf("%s:%s", host, port))
680
681
682
	if err != nil {
		return err
	}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
683

684
685
686
687
688
	var origins []string
	if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
		origins = strings.Split(o, ",")
	}

689
690
691
692
693
694
	if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
		if err := server.PruneLayers(); err != nil {
			return err
		}
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
695
	return server.Serve(ln, origins)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
696
697
}

698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
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
		}

Michael Yang's avatar
Michael Yang committed
720
		err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
721
722
723
724
		if err != nil {
			return fmt.Errorf("could not create directory %w", err)
		}

725
		err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
726
727
728
729
730
731
732
733
734
735
736
		if err != nil {
			return err
		}

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

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

737
		err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
738
739
740
741
742
743
744
745
746
		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
747
func startMacApp(client *api.Client) error {
Bruce MacDonald's avatar
Bruce MacDonald committed
748
749
750
751
752
	exe, err := os.Executable()
	if err != nil {
		return err
	}
	link, err := os.Readlink(exe)
Bruce MacDonald's avatar
Bruce MacDonald committed
753
754
755
	if err != nil {
		return err
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
756
757
758
	if !strings.Contains(link, "Ollama.app") {
		return fmt.Errorf("could not find ollama app")
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
	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
			}
		}
	}
}

778
func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
779
780
781
782
	client, err := api.FromEnv()
	if err != nil {
		return err
	}
783
	if err := client.Heartbeat(context.Background()); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
784
785
786
787
788
		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
789
				return fmt.Errorf("could not connect to ollama app, is it running?")
790
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
791
		} else {
792
793
794
795
796
797
			return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
		}
	}
	return nil
}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
798
799
800
801
func NewCLI() *cobra.Command {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	rootCmd := &cobra.Command{
802
803
804
805
		Use:           "ollama",
		Short:         "Large language model runner",
		SilenceUsage:  true,
		SilenceErrors: true,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
806
807
808
		CompletionOptions: cobra.CompletionOptions{
			DisableDefaultCmd: true,
		},
Michael Yang's avatar
Michael Yang committed
809
		Version: version.Version,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
810
811
812
813
	}

	cobra.EnableCommandSorting = false

814
	createCmd := &cobra.Command{
815
816
817
818
819
		Use:     "create MODEL",
		Short:   "Create a model from a Modelfile",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    CreateHandler,
820
821
822
823
	}

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

Patrick Devine's avatar
Patrick Devine committed
824
825
826
827
828
829
830
831
832
833
834
835
836
837
	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
838
	runCmd := &cobra.Command{
839
840
841
842
843
		Use:     "run MODEL [PROMPT]",
		Short:   "Run a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    RunHandler,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
844
845
	}

846
	runCmd.Flags().Bool("verbose", false, "Show timings for response")
847
	runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
848

Jeffrey Morgan's avatar
Jeffrey Morgan committed
849
850
851
852
	serveCmd := &cobra.Command{
		Use:     "serve",
		Aliases: []string{"start"},
		Short:   "Start ollama",
Michael Yang's avatar
Michael Yang committed
853
		RunE:    RunServer,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
854
855
	}

856
	pullCmd := &cobra.Command{
857
858
859
860
861
		Use:     "pull MODEL",
		Short:   "Pull a model from a registry",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    PullHandler,
862
863
	}

864
865
	pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")

866
	pushCmd := &cobra.Command{
867
868
869
870
871
		Use:     "push MODEL",
		Short:   "Push a model to a registry",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    PushHandler,
872
873
	}

874
875
	pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")

Patrick Devine's avatar
Patrick Devine committed
876
	listCmd := &cobra.Command{
877
		Use:     "list",
Patrick Devine's avatar
Patrick Devine committed
878
		Aliases: []string{"ls"},
879
		Short:   "List models",
880
		PreRunE: checkServerHeartbeat,
881
		RunE:    ListHandler,
882
883
	}

Patrick Devine's avatar
Patrick Devine committed
884
	copyCmd := &cobra.Command{
885
886
887
888
889
		Use:     "cp",
		Short:   "Copy a model",
		Args:    cobra.MinimumNArgs(2),
		PreRunE: checkServerHeartbeat,
		RunE:    CopyHandler,
Patrick Devine's avatar
Patrick Devine committed
890
891
	}

892
	deleteCmd := &cobra.Command{
893
894
895
896
897
		Use:     "rm",
		Short:   "Remove a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    DeleteHandler,
Patrick Devine's avatar
Patrick Devine committed
898
899
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
900
901
	rootCmd.AddCommand(
		serveCmd,
902
		createCmd,
Patrick Devine's avatar
Patrick Devine committed
903
		showCmd,
904
		runCmd,
905
906
		pullCmd,
		pushCmd,
Patrick Devine's avatar
Patrick Devine committed
907
		listCmd,
Patrick Devine's avatar
Patrick Devine committed
908
		copyCmd,
909
		deleteCmd,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
910
911
912
913
	)

	return rootCmd
}