cmd.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. package cmd
  2. import (
  3. "bufio"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "log"
  8. "net"
  9. "net/http"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "time"
  14. "github.com/schollz/progressbar/v3"
  15. "github.com/spf13/cobra"
  16. "golang.org/x/term"
  17. "github.com/jmorganca/ollama/api"
  18. "github.com/jmorganca/ollama/server"
  19. )
  20. func cacheDir() string {
  21. home, err := os.UserHomeDir()
  22. if err != nil {
  23. panic(err)
  24. }
  25. return filepath.Join(home, ".ollama")
  26. }
  27. func create(cmd *cobra.Command, args []string) error {
  28. filename, _ := cmd.Flags().GetString("file")
  29. client := api.NewClient()
  30. request := api.CreateRequest{Name: args[0], Path: filename}
  31. fn := func(resp api.CreateProgress) error {
  32. fmt.Println(resp.Status)
  33. return nil
  34. }
  35. if err := client.Create(context.Background(), &request, fn); err != nil {
  36. return err
  37. }
  38. return nil
  39. }
  40. func RunRun(cmd *cobra.Command, args []string) error {
  41. mp := server.ParseModelPath(args[0])
  42. fp, err := mp.GetManifestPath(false)
  43. if err != nil {
  44. return err
  45. }
  46. _, err = os.Stat(fp)
  47. switch {
  48. case errors.Is(err, os.ErrNotExist):
  49. if err := pull(args[0]); err != nil {
  50. var apiStatusError api.StatusError
  51. if !errors.As(err, &apiStatusError) {
  52. return err
  53. }
  54. if apiStatusError.StatusCode != http.StatusBadGateway {
  55. return err
  56. }
  57. }
  58. case err != nil:
  59. return err
  60. }
  61. return RunGenerate(cmd, args)
  62. }
  63. func push(cmd *cobra.Command, args []string) error {
  64. client := api.NewClient()
  65. request := api.PushRequest{Name: args[0]}
  66. fn := func(resp api.PushProgress) error {
  67. fmt.Println(resp.Status)
  68. return nil
  69. }
  70. if err := client.Push(context.Background(), &request, fn); err != nil {
  71. return err
  72. }
  73. return nil
  74. }
  75. func RunPull(cmd *cobra.Command, args []string) error {
  76. return pull(args[0])
  77. }
  78. func pull(model string) error {
  79. client := api.NewClient()
  80. var bar *progressbar.ProgressBar
  81. currentLayer := ""
  82. request := api.PullRequest{Name: model}
  83. fn := func(resp api.PullProgress) error {
  84. if resp.Digest != currentLayer && resp.Digest != "" {
  85. if currentLayer != "" {
  86. fmt.Println()
  87. }
  88. currentLayer = resp.Digest
  89. layerStr := resp.Digest[7:23] + "..."
  90. bar = progressbar.DefaultBytes(
  91. int64(resp.Total),
  92. "pulling "+layerStr,
  93. )
  94. } else if resp.Digest == currentLayer && resp.Digest != "" {
  95. bar.Set(resp.Completed)
  96. } else {
  97. currentLayer = ""
  98. fmt.Println(resp.Status)
  99. }
  100. return nil
  101. }
  102. if err := client.Pull(context.Background(), &request, fn); err != nil {
  103. return err
  104. }
  105. return nil
  106. }
  107. func RunGenerate(cmd *cobra.Command, args []string) error {
  108. if len(args) > 1 {
  109. // join all args into a single prompt
  110. return generate(cmd, args[0], strings.Join(args[1:], " "))
  111. }
  112. if term.IsTerminal(int(os.Stdin.Fd())) {
  113. return generateInteractive(cmd, args[0])
  114. }
  115. return generateBatch(cmd, args[0])
  116. }
  117. var generateContextKey struct{}
  118. func generate(cmd *cobra.Command, model, prompt string) error {
  119. if len(strings.TrimSpace(prompt)) > 0 {
  120. client := api.NewClient()
  121. spinner := progressbar.NewOptions(-1,
  122. progressbar.OptionSetWriter(os.Stderr),
  123. progressbar.OptionThrottle(60*time.Millisecond),
  124. progressbar.OptionSpinnerType(14),
  125. progressbar.OptionSetRenderBlankState(true),
  126. progressbar.OptionSetElapsedTime(false),
  127. progressbar.OptionClearOnFinish(),
  128. )
  129. go func() {
  130. for range time.Tick(60 * time.Millisecond) {
  131. if spinner.IsFinished() {
  132. break
  133. }
  134. spinner.Add(1)
  135. }
  136. }()
  137. var latest api.GenerateResponse
  138. generateContext, ok := cmd.Context().Value(generateContextKey).([]int)
  139. if !ok {
  140. generateContext = []int{}
  141. }
  142. request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
  143. fn := func(resp api.GenerateResponse) error {
  144. if !spinner.IsFinished() {
  145. spinner.Finish()
  146. }
  147. latest = resp
  148. fmt.Print(resp.Response)
  149. cmd.SetContext(context.WithValue(cmd.Context(), generateContextKey, resp.Context))
  150. return nil
  151. }
  152. if err := client.Generate(context.Background(), &request, fn); err != nil {
  153. return err
  154. }
  155. fmt.Println()
  156. fmt.Println()
  157. verbose, err := cmd.Flags().GetBool("verbose")
  158. if err != nil {
  159. return err
  160. }
  161. if verbose {
  162. latest.Summary()
  163. }
  164. }
  165. return nil
  166. }
  167. func generateInteractive(cmd *cobra.Command, model string) error {
  168. fmt.Print(">>> ")
  169. scanner := bufio.NewScanner(os.Stdin)
  170. for scanner.Scan() {
  171. if err := generate(cmd, model, scanner.Text()); err != nil {
  172. return err
  173. }
  174. fmt.Print(">>> ")
  175. }
  176. return nil
  177. }
  178. func generateBatch(cmd *cobra.Command, model string) error {
  179. scanner := bufio.NewScanner(os.Stdin)
  180. for scanner.Scan() {
  181. prompt := scanner.Text()
  182. fmt.Printf(">>> %s\n", prompt)
  183. if err := generate(cmd, model, prompt); err != nil {
  184. return err
  185. }
  186. }
  187. return nil
  188. }
  189. func RunServer(_ *cobra.Command, _ []string) error {
  190. host := os.Getenv("OLLAMA_HOST")
  191. if host == "" {
  192. host = "127.0.0.1"
  193. }
  194. port := os.Getenv("OLLAMA_PORT")
  195. if port == "" {
  196. port = "11434"
  197. }
  198. ln, err := net.Listen("tcp", fmt.Sprintf("%s:%s", host, port))
  199. if err != nil {
  200. return err
  201. }
  202. return server.Serve(ln)
  203. }
  204. func NewCLI() *cobra.Command {
  205. log.SetFlags(log.LstdFlags | log.Lshortfile)
  206. rootCmd := &cobra.Command{
  207. Use: "ollama",
  208. Short: "Large language model runner",
  209. SilenceUsage: true,
  210. CompletionOptions: cobra.CompletionOptions{
  211. DisableDefaultCmd: true,
  212. },
  213. PersistentPreRunE: func(_ *cobra.Command, args []string) error {
  214. // create the models directory and it's parent
  215. return os.MkdirAll(filepath.Join(cacheDir(), "models"), 0o700)
  216. },
  217. }
  218. cobra.EnableCommandSorting = false
  219. createCmd := &cobra.Command{
  220. Use: "create MODEL",
  221. Short: "Create a model from a Modelfile",
  222. Args: cobra.MinimumNArgs(1),
  223. RunE: create,
  224. }
  225. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  226. runCmd := &cobra.Command{
  227. Use: "run MODEL [PROMPT]",
  228. Short: "Run a model",
  229. Args: cobra.MinimumNArgs(1),
  230. RunE: RunRun,
  231. }
  232. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  233. serveCmd := &cobra.Command{
  234. Use: "serve",
  235. Aliases: []string{"start"},
  236. Short: "Start ollama",
  237. RunE: RunServer,
  238. }
  239. pullCmd := &cobra.Command{
  240. Use: "pull MODEL",
  241. Short: "Pull a model from a registry",
  242. Args: cobra.MinimumNArgs(1),
  243. RunE: RunPull,
  244. }
  245. pushCmd := &cobra.Command{
  246. Use: "push MODEL",
  247. Short: "Push a model to a registry",
  248. Args: cobra.MinimumNArgs(1),
  249. RunE: push,
  250. }
  251. rootCmd.AddCommand(
  252. serveCmd,
  253. createCmd,
  254. runCmd,
  255. pullCmd,
  256. pushCmd,
  257. )
  258. return rootCmd
  259. }