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

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

Patrick Devine's avatar
Patrick Devine committed
25
	"github.com/olekukonko/tablewriter"
Michael Yang's avatar
Michael Yang committed
26
	"github.com/spf13/cobra"
27
	"golang.org/x/crypto/ssh"
28
	"golang.org/x/term"
Michael Yang's avatar
Michael Yang committed
29

Jeffrey Morgan's avatar
Jeffrey Morgan committed
30
	"github.com/jmorganca/ollama/api"
Patrick Devine's avatar
Patrick Devine committed
31
	"github.com/jmorganca/ollama/format"
Michael Yang's avatar
Michael Yang committed
32
	"github.com/jmorganca/ollama/parser"
Michael Yang's avatar
Michael Yang committed
33
	"github.com/jmorganca/ollama/progress"
Patrick Devine's avatar
Patrick Devine committed
34
	"github.com/jmorganca/ollama/readline"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
35
	"github.com/jmorganca/ollama/server"
Michael Yang's avatar
Michael Yang committed
36
	"github.com/jmorganca/ollama/version"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
37
38
)

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

Michael Yang's avatar
Michael Yang committed
46
	client, err := api.ClientFromEnvironment()
47
48
49
	if err != nil {
		return err
	}
50

Michael Yang's avatar
Michael Yang committed
51
52
53
54
55
	p := progress.NewProgress(os.Stderr)
	defer p.Stop()

	bars := make(map[string]*progress.Bar)

Michael Yang's avatar
Michael Yang committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	modelfile, err := os.ReadFile(filename)
	if err != nil {
		return err
	}

	commands, err := parser.Parse(bytes.NewReader(modelfile))
	if err != nil {
		return err
	}

	home, err := os.UserHomeDir()
	if err != nil {
		return err
	}

71
72
	status := "transferring model data"
	spinner := progress.NewSpinner(status)
73
74
	p.Add(status, spinner)

Michael Yang's avatar
Michael Yang committed
75
76
77
78
79
80
81
82
83
84
85
86
	for _, c := range commands {
		switch c.Name {
		case "model", "adapter":
			path := c.Args
			if path == "~" {
				path = home
			} else if strings.HasPrefix(path, "~/") {
				path = filepath.Join(home, path[2:])
			}

			bin, err := os.Open(path)
			if errors.Is(err, os.ErrNotExist) && c.Name == "model" {
Michael Yang's avatar
Michael Yang committed
87
				continue
Michael Yang's avatar
Michael Yang committed
88
89
90
91
92
93
94
95
96
97
98
99
			} else if err != nil {
				return err
			}
			defer bin.Close()

			hash := sha256.New()
			if _, err := io.Copy(hash, bin); err != nil {
				return err
			}
			bin.Seek(0, io.SeekStart)

			digest := fmt.Sprintf("sha256:%x", hash.Sum(nil))
Michael Yang's avatar
Michael Yang committed
100
			if err = client.CreateBlob(cmd.Context(), digest, bin); err != nil {
Michael Yang's avatar
Michael Yang committed
101
102
103
				return err
			}

Michael Yang's avatar
Michael Yang committed
104
			modelfile = bytes.ReplaceAll(modelfile, []byte(c.Args), []byte("@"+digest))
Michael Yang's avatar
Michael Yang committed
105
106
		}
	}
Michael Yang's avatar
Michael Yang committed
107

108
	fn := func(resp api.ProgressResponse) error {
Michael Yang's avatar
Michael Yang committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
		if resp.Digest != "" {
			spinner.Stop()

			bar, ok := bars[resp.Digest]
			if !ok {
				bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
				bars[resp.Digest] = bar
				p.Add(resp.Digest, bar)
			}

			bar.Set(resp.Completed)
		} else if status != resp.Status {
			spinner.Stop()

			status = resp.Status
			spinner = progress.NewSpinner(status)
			p.Add(status, spinner)
		}

128
129
130
		return nil
	}

Michael Yang's avatar
Michael Yang committed
131
	request := api.CreateRequest{Name: args[0], Path: filename, Modelfile: string(modelfile)}
132
133
134
135
136
137
138
	if err := client.Create(context.Background(), &request, fn); err != nil {
		return err
	}

	return nil
}

139
func RunHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
140
	client, err := api.ClientFromEnvironment()
141
142
143
144
	if err != nil {
		return err
	}

145
146
147
	name := args[0]
	// check if the model exists on the server
	_, err = client.Show(context.Background(), &api.ShowRequest{Name: name})
Michael Yang's avatar
Michael Yang committed
148
149
150
151
	var statusError api.StatusError
	switch {
	case errors.As(err, &statusError) && statusError.StatusCode == http.StatusNotFound:
		if err := PullHandler(cmd, args); err != nil {
152
			return err
Michael Yang's avatar
Michael Yang committed
153
		}
Michael Yang's avatar
Michael Yang committed
154
155
	case err != nil:
		return err
156
157
	}

Michael Yang's avatar
Michael Yang committed
158
	return RunGenerate(cmd, args)
Bruce MacDonald's avatar
Bruce MacDonald committed
159
160
}

161
func PushHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
162
	client, err := api.ClientFromEnvironment()
163
164
165
	if err != nil {
		return err
	}
166

167
168
169
170
171
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
172
173
174
175
	p := progress.NewProgress(os.Stderr)
	defer p.Stop()

	bars := make(map[string]*progress.Bar)
176
177
	var status string
	var spinner *progress.Spinner
Michael Yang's avatar
Michael Yang committed
178

179
	fn := func(resp api.ProgressResponse) error {
Michael Yang's avatar
Michael Yang committed
180
		if resp.Digest != "" {
181
182
183
			if spinner != nil {
				spinner.Stop()
			}
Michael Yang's avatar
Michael Yang committed
184
185
186
187
188
189
190
191
192
193

			bar, ok := bars[resp.Digest]
			if !ok {
				bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
				bars[resp.Digest] = bar
				p.Add(resp.Digest, bar)
			}

			bar.Set(resp.Completed)
		} else if status != resp.Status {
194
195
196
			if spinner != nil {
				spinner.Stop()
			}
Michael Yang's avatar
Michael Yang committed
197
198
199
200
201
202

			status = resp.Status
			spinner = progress.NewSpinner(status)
			p.Add(status, spinner)
		}

203
204
205
		return nil
	}

Michael Yang's avatar
Michael Yang committed
206
207
208
209
210
	request := api.PushRequest{Name: args[0], Insecure: insecure}
	if err := client.Push(context.Background(), &request, fn); err != nil {
		return err
	}

211
	spinner.Stop()
Michael Yang's avatar
Michael Yang committed
212
	return nil
213
214
}

215
func ListHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
216
	client, err := api.ClientFromEnvironment()
217
218
219
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
220
221
222
223
224
225
226
227
228

	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
229
		if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
230
			data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), format.HumanTime(m.ModifiedAt, "Never")})
Michael Yang's avatar
Michael Yang committed
231
		}
Patrick Devine's avatar
Patrick Devine committed
232
233
234
	}

	table := tablewriter.NewWriter(os.Stdout)
Patrick Devine's avatar
Patrick Devine committed
235
	table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
Patrick Devine's avatar
Patrick Devine committed
236
237
238
239
240
241
242
243
244
245
246
247
	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
}

248
func DeleteHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
249
	client, err := api.ClientFromEnvironment()
250
251
252
	if err != nil {
		return err
	}
253

254
255
256
257
258
259
	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)
260
261
262
263
	}
	return nil
}

Patrick Devine's avatar
Patrick Devine committed
264
func ShowHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
265
	client, err := api.ClientFromEnvironment()
Patrick Devine's avatar
Patrick Devine committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
	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 {
315
		return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
Patrick Devine's avatar
Patrick Devine committed
316
	} else if flagsSet == 0 {
317
		return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
Patrick Devine's avatar
Patrick Devine committed
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
	}

	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
342
func CopyHandler(cmd *cobra.Command, args []string) error {
Michael Yang's avatar
Michael Yang committed
343
	client, err := api.ClientFromEnvironment()
344
345
346
	if err != nil {
		return err
	}
Patrick Devine's avatar
Patrick Devine committed
347
348
349
350
351
352
353
354
355

	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
}

356
func PullHandler(cmd *cobra.Command, args []string) error {
357
358
359
360
361
	insecure, err := cmd.Flags().GetBool("insecure")
	if err != nil {
		return err
	}

Michael Yang's avatar
Michael Yang committed
362
	client, err := api.ClientFromEnvironment()
363
364
365
	if err != nil {
		return err
	}
366

Michael Yang's avatar
Michael Yang committed
367
368
369
370
371
	p := progress.NewProgress(os.Stderr)
	defer p.Stop()

	bars := make(map[string]*progress.Bar)

372
373
	var status string
	var spinner *progress.Spinner
Michael Yang's avatar
Michael Yang committed
374

375
	fn := func(resp api.ProgressResponse) error {
Michael Yang's avatar
Michael Yang committed
376
		if resp.Digest != "" {
377
378
379
			if spinner != nil {
				spinner.Stop()
			}
Michael Yang's avatar
Michael Yang committed
380
381
382
383
384
385
386
387
388
389

			bar, ok := bars[resp.Digest]
			if !ok {
				bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
				bars[resp.Digest] = bar
				p.Add(resp.Digest, bar)
			}

			bar.Set(resp.Completed)
		} else if status != resp.Status {
390
391
392
			if spinner != nil {
				spinner.Stop()
			}
Michael Yang's avatar
Michael Yang committed
393
394
395
396
397
398

			status = resp.Status
			spinner = progress.NewSpinner(status)
			p.Add(status, spinner)
		}

399
400
		return nil
	}
401

Michael Yang's avatar
Michael Yang committed
402
403
404
405
406
407
	request := api.PullRequest{Name: args[0], Insecure: insecure}
	if err := client.Pull(context.Background(), &request, fn); err != nil {
		return err
	}

	return nil
Michael Yang's avatar
Michael Yang committed
408
409
}

410
func RunGenerate(cmd *cobra.Command, args []string) error {
411
412
413
414
	format, err := cmd.Flags().GetString("format")
	if err != nil {
		return err
	}
415

416
	prompts := args[1:]
417

418
419
420
	// prepend stdin to the prompt if provided
	if !term.IsTerminal(int(os.Stdin.Fd())) {
		in, err := io.ReadAll(os.Stdin)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
421
422
423
424
		if err != nil {
			return err
		}

425
		prompts = append([]string{string(in)}, prompts...)
Michael Yang's avatar
Michael Yang committed
426
427
	}

428
429
430
	// output is being piped
	if !term.IsTerminal(int(os.Stdout.Fd())) {
		return generate(cmd, args[0], strings.Join(prompts, " "), false, format)
Michael Yang's avatar
Michael Yang committed
431
432
	}

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
	wordWrap := os.Getenv("TERM") == "xterm-256color"

	nowrap, err := cmd.Flags().GetBool("nowordwrap")
	if err != nil {
		return err
	}
	if nowrap {
		wordWrap = false
	}

	// prompts are provided via stdin or args so don't enter interactive mode
	if len(prompts) > 0 {
		return generate(cmd, args[0], strings.Join(prompts, " "), wordWrap, format)
	}

	return generateInteractive(cmd, args[0], wordWrap, format)
Michael Yang's avatar
Michael Yang committed
449
450
}

Michael Yang's avatar
Michael Yang committed
451
type generateContextKey string
Michael Yang's avatar
Michael Yang committed
452

Jeffrey Morgan's avatar
Jeffrey Morgan committed
453
func generate(cmd *cobra.Command, model, prompt string, wordWrap bool, format string) error {
Michael Yang's avatar
Michael Yang committed
454
	client, err := api.ClientFromEnvironment()
Patrick Devine's avatar
Patrick Devine committed
455
456
457
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
458

Michael Yang's avatar
Michael Yang committed
459
460
461
462
463
464
	p := progress.NewProgress(os.Stderr)
	defer p.Stop()

	spinner := progress.NewSpinner("")
	p.Add("", spinner)

Patrick Devine's avatar
Patrick Devine committed
465
	var latest api.GenerateResponse
466

Patrick Devine's avatar
Patrick Devine committed
467
468
469
470
	generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
	if !ok {
		generateContext = []int{}
	}
Michael Yang's avatar
Michael Yang committed
471

472
	termWidth, _, err := term.GetSize(int(os.Stdout.Fd()))
473
	if err != nil {
474
		wordWrap = false
475
476
	}

477
478
479
480
481
482
483
484
485
486
487
488
489
	cancelCtx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT)
	var abort bool

	go func() {
		<-sigChan
		cancel()
		abort = true
	}()

490
491
492
	var currentLineLength int
	var wordBuffer string

Jeffrey Morgan's avatar
Jeffrey Morgan committed
493
	request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext, Format: format}
Patrick Devine's avatar
Patrick Devine committed
494
	fn := func(response api.GenerateResponse) error {
Michael Yang's avatar
Michael Yang committed
495
496
497
		spinner.Stop()
		p.StopAndClear()

Patrick Devine's avatar
Patrick Devine committed
498
		latest = response
499

500
		if wordWrap {
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
			for _, ch := range response.Response {
				if currentLineLength+1 > termWidth-5 {
					// backtrack the length of the last word and clear to the end of the line
					fmt.Printf("\x1b[%dD\x1b[K\n", len(wordBuffer))
					fmt.Printf("%s%c", wordBuffer, ch)
					currentLineLength = len(wordBuffer) + 1
				} else {
					fmt.Print(string(ch))
					currentLineLength += 1

					switch ch {
					case ' ':
						wordBuffer = ""
					case '\n':
						currentLineLength = 0
					default:
						wordBuffer += string(ch)
					}
				}
			}
		} else {
			fmt.Print(response.Response)
		}

Patrick Devine's avatar
Patrick Devine committed
525
526
		return nil
	}
527

528
	if err := client.Generate(cancelCtx, &request, fn); err != nil {
529
		if strings.Contains(err.Error(), "context canceled") && abort {
530
			return nil
531
		}
Patrick Devine's avatar
Patrick Devine committed
532
533
534
		return err
	}
	if prompt != "" {
Michael Yang's avatar
Michael Yang committed
535
536
		fmt.Println()
		fmt.Println()
Patrick Devine's avatar
Patrick Devine committed
537
	}
538

Patrick Devine's avatar
Patrick Devine committed
539
	if !latest.Done {
540
541
542
		if abort {
			return nil
		}
Patrick Devine's avatar
Patrick Devine committed
543
544
		return errors.New("unexpected end of response")
	}
545

Patrick Devine's avatar
Patrick Devine committed
546
547
548
549
	verbose, err := cmd.Flags().GetBool("verbose")
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
550

Patrick Devine's avatar
Patrick Devine committed
551
552
	if verbose {
		latest.Summary()
Michael Yang's avatar
Michael Yang committed
553
	}
Michael Yang's avatar
Michael Yang committed
554

Patrick Devine's avatar
Patrick Devine committed
555
556
557
558
	ctx := cmd.Context()
	ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
	cmd.SetContext(ctx)

Michael Yang's avatar
Michael Yang committed
559
560
561
	return nil
}

562
func generateInteractive(cmd *cobra.Command, model string, wordWrap bool, format string) error {
Patrick Devine's avatar
Patrick Devine committed
563
	// load the model
Jeffrey Morgan's avatar
Jeffrey Morgan committed
564
	if err := generate(cmd, model, "", false, ""); err != nil {
Patrick Devine's avatar
Patrick Devine committed
565
566
567
		return err
	}

Michael Yang's avatar
Michael Yang committed
568
	usage := func() {
Patrick Devine's avatar
Patrick Devine committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
		fmt.Fprintln(os.Stderr, "Available Commands:")
		fmt.Fprintln(os.Stderr, "  /set         Set session variables")
		fmt.Fprintln(os.Stderr, "  /show        Show model information")
		fmt.Fprintln(os.Stderr, "  /bye         Exit")
		fmt.Fprintln(os.Stderr, "  /?, /help    Help for a command")
		fmt.Fprintln(os.Stderr, "")
		fmt.Fprintln(os.Stderr, "Use \"\"\" to begin a multi-line message.")
		fmt.Fprintln(os.Stderr, "")
	}

	usageSet := func() {
		fmt.Fprintln(os.Stderr, "Available Commands:")
		fmt.Fprintln(os.Stderr, "  /set history      Enable history")
		fmt.Fprintln(os.Stderr, "  /set nohistory    Disable history")
		fmt.Fprintln(os.Stderr, "  /set wordwrap     Enable wordwrap")
		fmt.Fprintln(os.Stderr, "  /set nowordwrap   Disable wordwrap")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
585
586
		fmt.Fprintln(os.Stderr, "  /set format json  Enable JSON mode")
		fmt.Fprintln(os.Stderr, "  /set noformat     Disable formatting")
Patrick Devine's avatar
Patrick Devine committed
587
588
589
590
591
592
593
594
595
596
597
598
599
		fmt.Fprintln(os.Stderr, "  /set verbose      Show LLM stats")
		fmt.Fprintln(os.Stderr, "  /set quiet        Disable LLM stats")
		fmt.Fprintln(os.Stderr, "")
	}

	usageShow := func() {
		fmt.Fprintln(os.Stderr, "Available Commands:")
		fmt.Fprintln(os.Stderr, "  /show license      Show model license")
		fmt.Fprintln(os.Stderr, "  /show modelfile    Show Modelfile for this model")
		fmt.Fprintln(os.Stderr, "  /show parameters   Show parameters for this model")
		fmt.Fprintln(os.Stderr, "  /show system       Show system prompt")
		fmt.Fprintln(os.Stderr, "  /show template     Show prompt template")
		fmt.Fprintln(os.Stderr, "")
Michael Yang's avatar
Michael Yang committed
600
601
	}

Patrick Devine's avatar
Patrick Devine committed
602
603
604
605
606
	prompt := readline.Prompt{
		Prompt:         ">>> ",
		AltPrompt:      "... ",
		Placeholder:    "Send a message (/? for help)",
		AltPlaceholder: `Use """ to end multi-line input`,
Michael Yang's avatar
Michael Yang committed
607
608
	}

Patrick Devine's avatar
Patrick Devine committed
609
	scanner, err := readline.New(prompt)
Michael Yang's avatar
Michael Yang committed
610
611
612
613
	if err != nil {
		return err
	}

614
615
616
	fmt.Print(readline.StartBracketedPaste)
	defer fmt.Printf(readline.EndBracketedPaste)

617
618
	var multiLineBuffer string

Michael Yang's avatar
Michael Yang committed
619
620
621
622
	for {
		line, err := scanner.Readline()
		switch {
		case errors.Is(err, io.EOF):
623
			fmt.Println()
Michael Yang's avatar
Michael Yang committed
624
625
			return nil
		case errors.Is(err, readline.ErrInterrupt):
626
			if line == "" {
Patrick Devine's avatar
Patrick Devine committed
627
				fmt.Println("\nUse Ctrl-D or /bye to exit.")
628
629
			}

Michael Yang's avatar
Michael Yang committed
630
631
			continue
		case err != nil:
Michael Yang's avatar
Michael Yang committed
632
633
634
			return err
		}

Michael Yang's avatar
Michael Yang committed
635
		line = strings.TrimSpace(line)
Michael Yang's avatar
Michael Yang committed
636

Michael Yang's avatar
Michael Yang committed
637
		switch {
Patrick Devine's avatar
Patrick Devine committed
638
		case scanner.Prompt.UseAlt:
639
			if strings.HasSuffix(line, `"""`) {
Patrick Devine's avatar
Patrick Devine committed
640
				scanner.Prompt.UseAlt = false
641
				multiLineBuffer += strings.TrimSuffix(line, `"""`)
642
				line = multiLineBuffer
643
644
645
646
647
648
				multiLineBuffer = ""
			} else {
				multiLineBuffer += line + " "
				continue
			}
		case strings.HasPrefix(line, `"""`):
Patrick Devine's avatar
Patrick Devine committed
649
			scanner.Prompt.UseAlt = true
650
651
			multiLineBuffer = strings.TrimPrefix(line, `"""`) + " "
			continue
Michael Yang's avatar
Michael Yang committed
652
653
		case strings.HasPrefix(line, "/list"):
			args := strings.Fields(line)
654
			if err := ListHandler(cmd, args[1:]); err != nil {
Michael Yang's avatar
Michael Yang committed
655
656
657
658
659
660
661
662
663
664
				return err
			}
		case strings.HasPrefix(line, "/set"):
			args := strings.Fields(line)
			if len(args) > 1 {
				switch args[1] {
				case "history":
					scanner.HistoryEnable()
				case "nohistory":
					scanner.HistoryDisable()
665
				case "wordwrap":
666
					wordWrap = true
667
668
					fmt.Println("Set 'wordwrap' mode.")
				case "nowordwrap":
669
					wordWrap = false
670
					fmt.Println("Set 'nowordwrap' mode.")
Michael Yang's avatar
Michael Yang committed
671
672
				case "verbose":
					cmd.Flags().Set("verbose", "true")
673
					fmt.Println("Set 'verbose' mode.")
Michael Yang's avatar
Michael Yang committed
674
675
				case "quiet":
					cmd.Flags().Set("verbose", "false")
676
					fmt.Println("Set 'quiet' mode.")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
677
678
679
680
681
682
683
684
685
686
				case "format":
					if len(args) < 3 || args[2] != "json" {
						fmt.Println("Invalid or missing format. For 'json' mode use '/set format json'")
					} else {
						format = args[2]
						fmt.Printf("Set format to '%s' mode.\n", args[2])
					}
				case "noformat":
					format = ""
					fmt.Println("Disabled format.")
687
688
				default:
					fmt.Printf("Unknown command '/set %s'. Type /? for help\n", args[1])
689
690
				}
			} else {
Patrick Devine's avatar
Patrick Devine committed
691
				usageSet()
692
693
694
695
			}
		case strings.HasPrefix(line, "/show"):
			args := strings.Fields(line)
			if len(args) > 1 {
696
697
698
699
700
701
				client, err := api.ClientFromEnvironment()
				if err != nil {
					fmt.Println("error: couldn't connect to ollama server")
					return err
				}
				resp, err := client.Show(cmd.Context(), &api.ShowRequest{Name: model})
702
				if err != nil {
Patrick Devine's avatar
Patrick Devine committed
703
					fmt.Println("error: couldn't get model")
704
					return err
705
				}
Patrick Devine's avatar
Patrick Devine committed
706

707
708
				switch args[1] {
				case "license":
709
					if resp.License == "" {
710
						fmt.Print("No license was specified for this model.\n\n")
711
712
713
					} else {
						fmt.Println(resp.License)
					}
Patrick Devine's avatar
Patrick Devine committed
714
715
716
				case "modelfile":
					fmt.Println(resp.Modelfile)
				case "parameters":
717
					if resp.Parameters == "" {
718
						fmt.Print("No parameters were specified for this model.\n\n")
719
720
721
					} else {
						fmt.Println(resp.Parameters)
					}
722
				case "system":
723
					if resp.System == "" {
724
						fmt.Print("No system prompt was specified for this model.\n\n")
725
726
727
					} else {
						fmt.Println(resp.System)
					}
728
				case "template":
729
					if resp.Template == "" {
730
						fmt.Print("No prompt template was specified for this model.\n\n")
731
732
733
					} else {
						fmt.Println(resp.Template)
					}
734
				default:
735
					fmt.Printf("Unknown command '/show %s'. Type /? for help\n", args[1])
Michael Yang's avatar
Michael Yang committed
736
				}
Patrick Devine's avatar
Patrick Devine committed
737
738
739
740
741
742
743
744
745
746
747
748
			} else {
				usageShow()
			}
		case strings.HasPrefix(line, "/help"), strings.HasPrefix(line, "/?"):
			args := strings.Fields(line)
			if len(args) > 1 {
				switch args[1] {
				case "set", "/set":
					usageSet()
				case "show", "/show":
					usageShow()
				}
749
750
			} else {
				usage()
Michael Yang's avatar
Michael Yang committed
751
752
753
			}
		case line == "/exit", line == "/bye":
			return nil
754
755
756
		case strings.HasPrefix(line, "/"):
			args := strings.Fields(line)
			fmt.Printf("Unknown command '%s'. Type /? for help\n", args[0])
Michael Yang's avatar
Michael Yang committed
757
758
		}

Patrick Devine's avatar
Patrick Devine committed
759
		if len(line) > 0 && line[0] != '/' {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
760
			if err := generate(cmd, model, line, wordWrap, format); err != nil {
Patrick Devine's avatar
Patrick Devine committed
761
762
				return err
			}
Michael Yang's avatar
Michael Yang committed
763
764
		}
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
765
766
}

767
func RunServer(cmd *cobra.Command, _ []string) error {
Michael Yang's avatar
Michael Yang committed
768
769
770
	host, port, err := net.SplitHostPort(os.Getenv("OLLAMA_HOST"))
	if err != nil {
		host, port = "127.0.0.1", "11434"
Michael Yang's avatar
Michael Yang committed
771
		if ip := net.ParseIP(strings.Trim(os.Getenv("OLLAMA_HOST"), "[]")); ip != nil {
Michael Yang's avatar
Michael Yang committed
772
773
			host = ip.String()
		}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
774
	}
775

Michael Yang's avatar
Michael Yang committed
776
	if err := initializeKeypair(); err != nil {
777
778
779
		return err
	}

Michael Yang's avatar
Michael Yang committed
780
	ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
781
782
783
	if err != nil {
		return err
	}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
784

785
786
787
788
789
	var origins []string
	if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
		origins = strings.Split(o, ",")
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
790
	return server.Serve(ln, origins)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
791
792
}

793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
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
815
		err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
816
817
818
819
		if err != nil {
			return fmt.Errorf("could not create directory %w", err)
		}

820
		err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
821
822
823
824
825
826
827
828
829
830
831
		if err != nil {
			return err
		}

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

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

832
		err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
833
834
835
836
837
838
839
840
841
		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
842
func startMacApp(client *api.Client) error {
Bruce MacDonald's avatar
Bruce MacDonald committed
843
844
845
846
847
	exe, err := os.Executable()
	if err != nil {
		return err
	}
	link, err := os.Readlink(exe)
Bruce MacDonald's avatar
Bruce MacDonald committed
848
849
850
	if err != nil {
		return err
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
851
852
853
	if !strings.Contains(link, "Ollama.app") {
		return fmt.Errorf("could not find ollama app")
	}
Bruce MacDonald's avatar
Bruce MacDonald committed
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
	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
			}
		}
	}
}

873
func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
Michael Yang's avatar
Michael Yang committed
874
	client, err := api.ClientFromEnvironment()
875
876
877
	if err != nil {
		return err
	}
878
	if err := client.Heartbeat(context.Background()); err != nil {
Bruce MacDonald's avatar
Bruce MacDonald committed
879
880
881
882
883
		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
884
				return fmt.Errorf("could not connect to ollama app, is it running?")
885
			}
Bruce MacDonald's avatar
Bruce MacDonald committed
886
		} else {
887
888
889
890
891
892
			return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
		}
	}
	return nil
}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
893
894
895
896
func NewCLI() *cobra.Command {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	rootCmd := &cobra.Command{
897
898
899
900
		Use:           "ollama",
		Short:         "Large language model runner",
		SilenceUsage:  true,
		SilenceErrors: true,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
901
902
903
		CompletionOptions: cobra.CompletionOptions{
			DisableDefaultCmd: true,
		},
Michael Yang's avatar
Michael Yang committed
904
		Version: version.Version,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
905
906
907
908
	}

	cobra.EnableCommandSorting = false

909
	createCmd := &cobra.Command{
910
911
		Use:     "create MODEL",
		Short:   "Create a model from a Modelfile",
Michael Yang's avatar
Michael Yang committed
912
		Args:    cobra.ExactArgs(1),
913
914
		PreRunE: checkServerHeartbeat,
		RunE:    CreateHandler,
915
916
917
918
	}

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

Patrick Devine's avatar
Patrick Devine committed
919
920
921
	showCmd := &cobra.Command{
		Use:     "show MODEL",
		Short:   "Show information for a model",
Michael Yang's avatar
Michael Yang committed
922
		Args:    cobra.ExactArgs(1),
Patrick Devine's avatar
Patrick Devine committed
923
924
925
926
927
928
929
930
931
932
		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
933
	runCmd := &cobra.Command{
934
935
936
937
938
		Use:     "run MODEL [PROMPT]",
		Short:   "Run a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    RunHandler,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
939
940
	}

941
	runCmd.Flags().Bool("verbose", false, "Show timings for response")
942
	runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
943
	runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
944
	runCmd.Flags().String("format", "", "Response format (e.g. json)")
945

Jeffrey Morgan's avatar
Jeffrey Morgan committed
946
947
948
949
	serveCmd := &cobra.Command{
		Use:     "serve",
		Aliases: []string{"start"},
		Short:   "Start ollama",
Michael Yang's avatar
Michael Yang committed
950
		Args:    cobra.ExactArgs(0),
Michael Yang's avatar
Michael Yang committed
951
		RunE:    RunServer,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
952
953
	}

954
	pullCmd := &cobra.Command{
955
956
		Use:     "pull MODEL",
		Short:   "Pull a model from a registry",
Michael Yang's avatar
Michael Yang committed
957
		Args:    cobra.ExactArgs(1),
958
959
		PreRunE: checkServerHeartbeat,
		RunE:    PullHandler,
960
961
	}

962
963
	pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")

964
	pushCmd := &cobra.Command{
965
966
		Use:     "push MODEL",
		Short:   "Push a model to a registry",
Michael Yang's avatar
Michael Yang committed
967
		Args:    cobra.ExactArgs(1),
968
969
		PreRunE: checkServerHeartbeat,
		RunE:    PushHandler,
970
971
	}

972
973
	pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")

Patrick Devine's avatar
Patrick Devine committed
974
	listCmd := &cobra.Command{
975
		Use:     "list",
Patrick Devine's avatar
Patrick Devine committed
976
		Aliases: []string{"ls"},
977
		Short:   "List models",
978
		PreRunE: checkServerHeartbeat,
979
		RunE:    ListHandler,
980
981
	}

Patrick Devine's avatar
Patrick Devine committed
982
	copyCmd := &cobra.Command{
Michael Yang's avatar
Michael Yang committed
983
		Use:     "cp SOURCE TARGET",
984
		Short:   "Copy a model",
Michael Yang's avatar
Michael Yang committed
985
		Args:    cobra.ExactArgs(2),
986
987
		PreRunE: checkServerHeartbeat,
		RunE:    CopyHandler,
Patrick Devine's avatar
Patrick Devine committed
988
989
	}

990
	deleteCmd := &cobra.Command{
Michael Yang's avatar
Michael Yang committed
991
		Use:     "rm MODEL [MODEL...]",
992
993
994
995
		Short:   "Remove a model",
		Args:    cobra.MinimumNArgs(1),
		PreRunE: checkServerHeartbeat,
		RunE:    DeleteHandler,
Patrick Devine's avatar
Patrick Devine committed
996
997
	}

Jeffrey Morgan's avatar
Jeffrey Morgan committed
998
999
	rootCmd.AddCommand(
		serveCmd,
1000
		createCmd,
Patrick Devine's avatar
Patrick Devine committed
1001
		showCmd,
1002
		runCmd,
1003
1004
		pullCmd,
		pushCmd,
Patrick Devine's avatar
Patrick Devine committed
1005
		listCmd,
Patrick Devine's avatar
Patrick Devine committed
1006
		copyCmd,
1007
		deleteCmd,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1008
1009
1010
1011
	)

	return rootCmd
}