cmd.go 3.07 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"
Bruce MacDonald's avatar
Bruce MacDonald committed
6
	"fmt"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
7
8
9
10
11
	"log"
	"net"
	"os"
	"path"

Michael Yang's avatar
Michael Yang committed
12
	"github.com/schollz/progressbar/v3"
Michael Yang's avatar
Michael Yang committed
13
14
15
	"github.com/spf13/cobra"
	"golang.org/x/term"

Jeffrey Morgan's avatar
Jeffrey Morgan committed
16
17
	"github.com/jmorganca/ollama/api"
	"github.com/jmorganca/ollama/server"
Jeffrey Morgan's avatar
Jeffrey Morgan committed
18
19
)

Bruce MacDonald's avatar
Bruce MacDonald committed
20
func cacheDir() string {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
21
22
23
24
25
	home, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
26
	return path.Join(home, ".ollama")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
27
28
}

Michael Yang's avatar
Michael Yang committed
29
30
31
32
33
34
35
func RunRun(cmd *cobra.Command, args []string) error {
	if err := pull(args[0]); err != nil {
		return err
	}

	fmt.Println("Up to date.")
	return RunGenerate(cmd, args)
Bruce MacDonald's avatar
Bruce MacDonald committed
36
37
}

Michael Yang's avatar
Michael Yang committed
38
func pull(model string) error {
Bruce MacDonald's avatar
Bruce MacDonald committed
39
40
41
42
	client, err := NewAPIClient()
	if err != nil {
		return err
	}
Michael Yang's avatar
Michael Yang committed
43
44
45
46
47
48
49
50
51
52
53
54
55

	var bar *progressbar.ProgressBar
	return client.Pull(
		context.Background(),
		&api.PullRequest{Model: model},
		func(progress api.PullProgress) error {
			if bar == nil {
				bar = progressbar.DefaultBytes(progress.Total)
			}

			return bar.Set64(progress.Completed)
		},
	)
Michael Yang's avatar
Michael Yang committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
}

func RunGenerate(_ *cobra.Command, args []string) error {
	if len(args) > 1 {
		return generate(args[0], args[1:]...)
	}

	if term.IsTerminal(int(os.Stdin.Fd())) {
		return generateInteractive(args[0])
	}

	return generateBatch(args[0])
}

func generate(model string, prompts ...string) error {
	client, err := NewAPIClient()
	if err != nil {
		return err
	}

	for _, prompt := range prompts {
Michael Yang's avatar
Michael Yang committed
77
		client.Generate(context.Background(), &api.GenerateRequest{Model: model, Prompt: prompt}, func(resp api.GenerateResponse) error {
Michael Yang's avatar
Michael Yang committed
78
			fmt.Print(resp.Response)
Michael Yang's avatar
Michael Yang committed
79
			return nil
Michael Yang's avatar
Michael Yang committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
		})
	}

	fmt.Println()
	fmt.Println()
	return nil
}

func generateInteractive(model string) error {
	fmt.Print(">>> ")
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if err := generate(model, scanner.Text()); err != nil {
			return err
		}

		fmt.Print(">>> ")
	}

Bruce MacDonald's avatar
Bruce MacDonald committed
99
	return nil
Bruce MacDonald's avatar
Bruce MacDonald committed
100
101
}

Michael Yang's avatar
Michael Yang committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
func generateBatch(model string) error {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		prompt := scanner.Text()
		fmt.Printf(">>> %s\n", prompt)
		if err := generate(model, prompt); err != nil {
			return err
		}
	}

	return nil
}

func RunServer(_ *cobra.Command, _ []string) error {
Michael Yang's avatar
Michael Yang committed
116
	ln, err := net.Listen("tcp", "127.0.0.1:11434")
Jeffrey Morgan's avatar
Jeffrey Morgan committed
117
118
119
120
121
122
123
124
	if err != nil {
		return err
	}

	return server.Serve(ln)
}

func NewAPIClient() (*api.Client, error) {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
125
	return &api.Client{
Michael Yang's avatar
Michael Yang committed
126
		URL: "http://localhost:11434",
Jeffrey Morgan's avatar
Jeffrey Morgan committed
127
128
129
130
131
132
133
	}, nil
}

func NewCLI() *cobra.Command {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	rootCmd := &cobra.Command{
Michael Yang's avatar
Michael Yang committed
134
135
136
		Use:          "ollama",
		Short:        "Large language model runner",
		SilenceUsage: true,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
137
138
139
		CompletionOptions: cobra.CompletionOptions{
			DisableDefaultCmd: true,
		},
Michael Yang's avatar
Michael Yang committed
140
		PersistentPreRunE: func(_ *cobra.Command, args []string) error {
Bruce MacDonald's avatar
Bruce MacDonald committed
141
			// create the models directory and it's parent
Michael Yang's avatar
Michael Yang committed
142
			return os.MkdirAll(path.Join(cacheDir(), "models"), 0o700)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
143
144
145
146
147
148
		},
	}

	cobra.EnableCommandSorting = false

	runCmd := &cobra.Command{
Michael Yang's avatar
Michael Yang committed
149
		Use:   "run MODEL [PROMPT]",
Jeffrey Morgan's avatar
Jeffrey Morgan committed
150
		Short: "Run a model",
Michael Yang's avatar
Michael Yang committed
151
152
		Args:  cobra.MinimumNArgs(1),
		RunE:  RunRun,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
153
154
155
156
157
158
	}

	serveCmd := &cobra.Command{
		Use:     "serve",
		Aliases: []string{"start"},
		Short:   "Start ollama",
Michael Yang's avatar
Michael Yang committed
159
		RunE:    RunServer,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
160
161
162
163
	}

	rootCmd.AddCommand(
		serveCmd,
164
		runCmd,
Jeffrey Morgan's avatar
Jeffrey Morgan committed
165
166
167
168
	)

	return rootCmd
}