cmd.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. package cmd
  2. import (
  3. "bufio"
  4. "context"
  5. "crypto/ed25519"
  6. "crypto/rand"
  7. "encoding/pem"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "log"
  12. "net"
  13. "net/http"
  14. "os"
  15. "os/exec"
  16. "path/filepath"
  17. "runtime"
  18. "strings"
  19. "time"
  20. "github.com/chzyer/readline"
  21. "github.com/dustin/go-humanize"
  22. "github.com/olekukonko/tablewriter"
  23. "github.com/spf13/cobra"
  24. "golang.org/x/crypto/ssh"
  25. "github.com/jmorganca/ollama/api"
  26. "github.com/jmorganca/ollama/format"
  27. "github.com/jmorganca/ollama/progressbar"
  28. "github.com/jmorganca/ollama/server"
  29. "github.com/jmorganca/ollama/version"
  30. )
  31. type Painter struct{}
  32. func (p Painter) Paint(line []rune, l int) []rune {
  33. termType := os.Getenv("TERM")
  34. if termType == "xterm-256color" && len(line) == 0 {
  35. prompt := "Send a message (/? for help)"
  36. return []rune(fmt.Sprintf("\033[38;5;245m%s\033[%dD\033[0m", prompt, len(prompt)))
  37. }
  38. return line
  39. }
  40. func CreateHandler(cmd *cobra.Command, args []string) error {
  41. filename, _ := cmd.Flags().GetString("file")
  42. filename, err := filepath.Abs(filename)
  43. if err != nil {
  44. return err
  45. }
  46. client, err := api.FromEnv()
  47. if err != nil {
  48. return err
  49. }
  50. var spinner *Spinner
  51. var currentDigest string
  52. var bar *progressbar.ProgressBar
  53. request := api.CreateRequest{Name: args[0], Path: filename}
  54. fn := func(resp api.ProgressResponse) error {
  55. if resp.Digest != currentDigest && resp.Digest != "" {
  56. if spinner != nil {
  57. spinner.Stop()
  58. }
  59. currentDigest = resp.Digest
  60. switch {
  61. case strings.Contains(resp.Status, "embeddings"):
  62. bar = progressbar.Default(int64(resp.Total), resp.Status)
  63. bar.Set(resp.Completed)
  64. default:
  65. // pulling
  66. bar = progressbar.DefaultBytes(
  67. int64(resp.Total),
  68. resp.Status,
  69. )
  70. bar.Set(resp.Completed)
  71. }
  72. } else if resp.Digest == currentDigest && resp.Digest != "" {
  73. bar.Set(resp.Completed)
  74. } else {
  75. currentDigest = ""
  76. if spinner != nil {
  77. spinner.Stop()
  78. }
  79. spinner = NewSpinner(resp.Status)
  80. go spinner.Spin(100 * time.Millisecond)
  81. }
  82. return nil
  83. }
  84. if err := client.Create(context.Background(), &request, fn); err != nil {
  85. return err
  86. }
  87. if spinner != nil {
  88. spinner.Stop()
  89. if spinner.description != "success" {
  90. return errors.New("unexpected end to create model")
  91. }
  92. }
  93. return nil
  94. }
  95. func RunHandler(cmd *cobra.Command, args []string) error {
  96. insecure, err := cmd.Flags().GetBool("insecure")
  97. if err != nil {
  98. return err
  99. }
  100. mp := server.ParseModelPath(args[0])
  101. if mp.ProtocolScheme == "http" && !insecure {
  102. return fmt.Errorf("insecure protocol http")
  103. }
  104. fp, err := mp.GetManifestPath(false)
  105. if err != nil {
  106. return err
  107. }
  108. _, err = os.Stat(fp)
  109. switch {
  110. case errors.Is(err, os.ErrNotExist):
  111. if err := pull(args[0], insecure); err != nil {
  112. var apiStatusError api.StatusError
  113. if !errors.As(err, &apiStatusError) {
  114. return err
  115. }
  116. if apiStatusError.StatusCode != http.StatusBadGateway {
  117. return err
  118. }
  119. }
  120. case err != nil:
  121. return err
  122. }
  123. return RunGenerate(cmd, args)
  124. }
  125. func PushHandler(cmd *cobra.Command, args []string) error {
  126. client, err := api.FromEnv()
  127. if err != nil {
  128. return err
  129. }
  130. insecure, err := cmd.Flags().GetBool("insecure")
  131. if err != nil {
  132. return err
  133. }
  134. var currentDigest string
  135. var bar *progressbar.ProgressBar
  136. request := api.PushRequest{Name: args[0], Insecure: insecure}
  137. fn := func(resp api.ProgressResponse) error {
  138. if resp.Digest != currentDigest && resp.Digest != "" {
  139. currentDigest = resp.Digest
  140. bar = progressbar.DefaultBytes(
  141. int64(resp.Total),
  142. fmt.Sprintf("pushing %s...", resp.Digest[7:19]),
  143. )
  144. bar.Set(resp.Completed)
  145. } else if resp.Digest == currentDigest && resp.Digest != "" {
  146. bar.Set(resp.Completed)
  147. } else {
  148. currentDigest = ""
  149. fmt.Println(resp.Status)
  150. }
  151. return nil
  152. }
  153. if err := client.Push(context.Background(), &request, fn); err != nil {
  154. return err
  155. }
  156. if bar != nil && !bar.IsFinished() {
  157. return errors.New("unexpected end to push model")
  158. }
  159. return nil
  160. }
  161. func ListHandler(cmd *cobra.Command, args []string) error {
  162. client, err := api.FromEnv()
  163. if err != nil {
  164. return err
  165. }
  166. models, err := client.List(context.Background())
  167. if err != nil {
  168. return err
  169. }
  170. var data [][]string
  171. for _, m := range models.Models {
  172. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  173. data = append(data, []string{m.Name, m.Digest[:12], humanize.Bytes(uint64(m.Size)), format.HumanTime(m.ModifiedAt, "Never")})
  174. }
  175. }
  176. table := tablewriter.NewWriter(os.Stdout)
  177. table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
  178. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  179. table.SetAlignment(tablewriter.ALIGN_LEFT)
  180. table.SetHeaderLine(false)
  181. table.SetBorder(false)
  182. table.SetNoWhiteSpace(true)
  183. table.SetTablePadding("\t")
  184. table.AppendBulk(data)
  185. table.Render()
  186. return nil
  187. }
  188. func DeleteHandler(cmd *cobra.Command, args []string) error {
  189. client, err := api.FromEnv()
  190. if err != nil {
  191. return err
  192. }
  193. for _, name := range args {
  194. req := api.DeleteRequest{Name: name}
  195. if err := client.Delete(context.Background(), &req); err != nil {
  196. return err
  197. }
  198. fmt.Printf("deleted '%s'\n", name)
  199. }
  200. return nil
  201. }
  202. func ShowHandler(cmd *cobra.Command, args []string) error {
  203. client, err := api.FromEnv()
  204. if err != nil {
  205. return err
  206. }
  207. if len(args) != 1 {
  208. return errors.New("missing model name")
  209. }
  210. license, errLicense := cmd.Flags().GetBool("license")
  211. modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
  212. parameters, errParams := cmd.Flags().GetBool("parameters")
  213. system, errSystem := cmd.Flags().GetBool("system")
  214. template, errTemplate := cmd.Flags().GetBool("template")
  215. for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
  216. if boolErr != nil {
  217. return errors.New("error retrieving flags")
  218. }
  219. }
  220. flagsSet := 0
  221. showType := ""
  222. if license {
  223. flagsSet++
  224. showType = "license"
  225. }
  226. if modelfile {
  227. flagsSet++
  228. showType = "modelfile"
  229. }
  230. if parameters {
  231. flagsSet++
  232. showType = "parameters"
  233. }
  234. if system {
  235. flagsSet++
  236. showType = "system"
  237. }
  238. if template {
  239. flagsSet++
  240. showType = "template"
  241. }
  242. if flagsSet > 1 {
  243. return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
  244. } else if flagsSet == 0 {
  245. return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
  246. }
  247. req := api.ShowRequest{Name: args[0]}
  248. resp, err := client.Show(context.Background(), &req)
  249. if err != nil {
  250. return err
  251. }
  252. switch showType {
  253. case "license":
  254. fmt.Println(resp.License)
  255. case "modelfile":
  256. fmt.Println(resp.Modelfile)
  257. case "parameters":
  258. fmt.Println(resp.Parameters)
  259. case "system":
  260. fmt.Println(resp.System)
  261. case "template":
  262. fmt.Println(resp.Template)
  263. }
  264. return nil
  265. }
  266. func CopyHandler(cmd *cobra.Command, args []string) error {
  267. client, err := api.FromEnv()
  268. if err != nil {
  269. return err
  270. }
  271. req := api.CopyRequest{Source: args[0], Destination: args[1]}
  272. if err := client.Copy(context.Background(), &req); err != nil {
  273. return err
  274. }
  275. fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
  276. return nil
  277. }
  278. func PullHandler(cmd *cobra.Command, args []string) error {
  279. insecure, err := cmd.Flags().GetBool("insecure")
  280. if err != nil {
  281. return err
  282. }
  283. return pull(args[0], insecure)
  284. }
  285. func pull(model string, insecure bool) error {
  286. client, err := api.FromEnv()
  287. if err != nil {
  288. return err
  289. }
  290. var currentDigest string
  291. var bar *progressbar.ProgressBar
  292. request := api.PullRequest{Name: model, Insecure: insecure}
  293. fn := func(resp api.ProgressResponse) error {
  294. if resp.Digest != currentDigest && resp.Digest != "" {
  295. currentDigest = resp.Digest
  296. bar = progressbar.DefaultBytes(
  297. int64(resp.Total),
  298. fmt.Sprintf("pulling %s...", resp.Digest[7:19]),
  299. )
  300. bar.Set(resp.Completed)
  301. } else if resp.Digest == currentDigest && resp.Digest != "" {
  302. bar.Set(resp.Completed)
  303. } else {
  304. currentDigest = ""
  305. fmt.Println(resp.Status)
  306. }
  307. return nil
  308. }
  309. if err := client.Pull(context.Background(), &request, fn); err != nil {
  310. return err
  311. }
  312. if bar != nil && !bar.IsFinished() {
  313. return errors.New("unexpected end to pull model")
  314. }
  315. return nil
  316. }
  317. func RunGenerate(cmd *cobra.Command, args []string) error {
  318. if len(args) > 1 {
  319. // join all args into a single prompt
  320. return generate(cmd, args[0], strings.Join(args[1:], " "))
  321. }
  322. if readline.IsTerminal(int(os.Stdin.Fd())) {
  323. return generateInteractive(cmd, args[0])
  324. }
  325. return generateBatch(cmd, args[0])
  326. }
  327. type generateContextKey string
  328. func generate(cmd *cobra.Command, model, prompt string) error {
  329. client, err := api.FromEnv()
  330. if err != nil {
  331. return err
  332. }
  333. spinner := NewSpinner("")
  334. go spinner.Spin(60 * time.Millisecond)
  335. var latest api.GenerateResponse
  336. generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
  337. if !ok {
  338. generateContext = []int{}
  339. }
  340. request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext}
  341. fn := func(response api.GenerateResponse) error {
  342. if !spinner.IsFinished() {
  343. spinner.Finish()
  344. }
  345. latest = response
  346. fmt.Print(response.Response)
  347. return nil
  348. }
  349. if err := client.Generate(context.Background(), &request, fn); err != nil {
  350. if strings.Contains(err.Error(), "failed to load model") {
  351. // tell the user to check the server log, if it exists locally
  352. home, nestedErr := os.UserHomeDir()
  353. if nestedErr != nil {
  354. // return the original error
  355. return err
  356. }
  357. logPath := filepath.Join(home, ".ollama", "logs", "server.log")
  358. if _, nestedErr := os.Stat(logPath); nestedErr == nil {
  359. err = fmt.Errorf("%w\nFor more details, check the error logs at %s", err, logPath)
  360. }
  361. }
  362. return err
  363. }
  364. if prompt != "" {
  365. fmt.Println()
  366. fmt.Println()
  367. }
  368. if !latest.Done {
  369. return errors.New("unexpected end of response")
  370. }
  371. verbose, err := cmd.Flags().GetBool("verbose")
  372. if err != nil {
  373. return err
  374. }
  375. if verbose {
  376. latest.Summary()
  377. }
  378. ctx := cmd.Context()
  379. ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
  380. cmd.SetContext(ctx)
  381. return nil
  382. }
  383. func generateInteractive(cmd *cobra.Command, model string) error {
  384. home, err := os.UserHomeDir()
  385. if err != nil {
  386. return err
  387. }
  388. // load the model
  389. if err := generate(cmd, model, ""); err != nil {
  390. return err
  391. }
  392. completer := readline.NewPrefixCompleter(
  393. readline.PcItem("/help"),
  394. readline.PcItem("/list"),
  395. readline.PcItem("/set",
  396. readline.PcItem("history"),
  397. readline.PcItem("nohistory"),
  398. readline.PcItem("verbose"),
  399. readline.PcItem("quiet"),
  400. readline.PcItem("mode",
  401. readline.PcItem("vim"),
  402. readline.PcItem("emacs"),
  403. readline.PcItem("default"),
  404. ),
  405. ),
  406. readline.PcItem("/show",
  407. readline.PcItem("license"),
  408. readline.PcItem("modelfile"),
  409. readline.PcItem("parameters"),
  410. readline.PcItem("system"),
  411. readline.PcItem("template"),
  412. ),
  413. readline.PcItem("/exit"),
  414. readline.PcItem("/bye"),
  415. )
  416. usage := func() {
  417. fmt.Fprintln(os.Stderr, "commands:")
  418. fmt.Fprintln(os.Stderr, completer.Tree(" "))
  419. }
  420. config := readline.Config{
  421. Painter: Painter{},
  422. Prompt: ">>> ",
  423. HistoryFile: filepath.Join(home, ".ollama", "history"),
  424. AutoComplete: completer,
  425. }
  426. scanner, err := readline.NewEx(&config)
  427. if err != nil {
  428. return err
  429. }
  430. defer scanner.Close()
  431. var multiLineBuffer string
  432. var isMultiLine bool
  433. for {
  434. line, err := scanner.Readline()
  435. switch {
  436. case errors.Is(err, io.EOF):
  437. return nil
  438. case errors.Is(err, readline.ErrInterrupt):
  439. if line == "" {
  440. return nil
  441. }
  442. continue
  443. case err != nil:
  444. return err
  445. }
  446. line = strings.TrimSpace(line)
  447. switch {
  448. case isMultiLine:
  449. if strings.HasSuffix(line, `"""`) {
  450. isMultiLine = false
  451. multiLineBuffer += strings.TrimSuffix(line, `"""`)
  452. line = multiLineBuffer
  453. multiLineBuffer = ""
  454. scanner.SetPrompt(">>> ")
  455. } else {
  456. multiLineBuffer += line + " "
  457. continue
  458. }
  459. case strings.HasPrefix(line, `"""`):
  460. isMultiLine = true
  461. multiLineBuffer = strings.TrimPrefix(line, `"""`) + " "
  462. scanner.SetPrompt("... ")
  463. continue
  464. case strings.HasPrefix(line, "/list"):
  465. args := strings.Fields(line)
  466. if err := ListHandler(cmd, args[1:]); err != nil {
  467. return err
  468. }
  469. continue
  470. case strings.HasPrefix(line, "/set"):
  471. args := strings.Fields(line)
  472. if len(args) > 1 {
  473. switch args[1] {
  474. case "history":
  475. scanner.HistoryEnable()
  476. continue
  477. case "nohistory":
  478. scanner.HistoryDisable()
  479. continue
  480. case "verbose":
  481. cmd.Flags().Set("verbose", "true")
  482. continue
  483. case "quiet":
  484. cmd.Flags().Set("verbose", "false")
  485. continue
  486. case "mode":
  487. if len(args) > 2 {
  488. switch args[2] {
  489. case "vim":
  490. scanner.SetVimMode(true)
  491. continue
  492. case "emacs", "default":
  493. scanner.SetVimMode(false)
  494. continue
  495. default:
  496. usage()
  497. continue
  498. }
  499. } else {
  500. usage()
  501. continue
  502. }
  503. }
  504. } else {
  505. usage()
  506. continue
  507. }
  508. case strings.HasPrefix(line, "/show"):
  509. args := strings.Fields(line)
  510. if len(args) > 1 {
  511. resp, err := server.GetModelInfo(model)
  512. if err != nil {
  513. fmt.Println("error: couldn't get model")
  514. continue
  515. }
  516. switch args[1] {
  517. case "license":
  518. fmt.Println(resp.License)
  519. case "modelfile":
  520. fmt.Println(resp.Modelfile)
  521. case "parameters":
  522. fmt.Println(resp.Parameters)
  523. case "system":
  524. fmt.Println(resp.System)
  525. case "template":
  526. fmt.Println(resp.Template)
  527. default:
  528. fmt.Println("error: unknown command")
  529. }
  530. continue
  531. } else {
  532. usage()
  533. continue
  534. }
  535. case line == "/help", line == "/?":
  536. usage()
  537. continue
  538. case line == "/exit", line == "/bye":
  539. return nil
  540. }
  541. if len(line) > 0 && line[0] != '/' {
  542. if err := generate(cmd, model, line); err != nil {
  543. return err
  544. }
  545. }
  546. }
  547. }
  548. func generateBatch(cmd *cobra.Command, model string) error {
  549. scanner := bufio.NewScanner(os.Stdin)
  550. for scanner.Scan() {
  551. prompt := scanner.Text()
  552. fmt.Printf(">>> %s\n", prompt)
  553. if err := generate(cmd, model, prompt); err != nil {
  554. return err
  555. }
  556. }
  557. return nil
  558. }
  559. func RunServer(cmd *cobra.Command, _ []string) error {
  560. host, port := "127.0.0.1", "11434"
  561. parts := strings.Split(os.Getenv("OLLAMA_HOST"), ":")
  562. if ip := net.ParseIP(parts[0]); ip != nil {
  563. host = ip.String()
  564. }
  565. if len(parts) > 1 {
  566. port = parts[1]
  567. }
  568. // deprecated: include port in OLLAMA_HOST
  569. if p := os.Getenv("OLLAMA_PORT"); p != "" {
  570. port = p
  571. }
  572. err := initializeKeypair()
  573. if err != nil {
  574. return err
  575. }
  576. ln, err := net.Listen("tcp", fmt.Sprintf("%s:%s", host, port))
  577. if err != nil {
  578. return err
  579. }
  580. var origins []string
  581. if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
  582. origins = strings.Split(o, ",")
  583. }
  584. if noprune := os.Getenv("OLLAMA_NOPRUNE"); noprune == "" {
  585. if err := server.PruneLayers(); err != nil {
  586. return err
  587. }
  588. }
  589. return server.Serve(ln, origins)
  590. }
  591. func initializeKeypair() error {
  592. home, err := os.UserHomeDir()
  593. if err != nil {
  594. return err
  595. }
  596. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  597. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  598. _, err = os.Stat(privKeyPath)
  599. if os.IsNotExist(err) {
  600. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  601. _, privKey, err := ed25519.GenerateKey(rand.Reader)
  602. if err != nil {
  603. return err
  604. }
  605. privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
  606. if err != nil {
  607. return err
  608. }
  609. err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
  610. if err != nil {
  611. return fmt.Errorf("could not create directory %w", err)
  612. }
  613. err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
  614. if err != nil {
  615. return err
  616. }
  617. sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
  618. if err != nil {
  619. return err
  620. }
  621. pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())
  622. err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
  623. if err != nil {
  624. return err
  625. }
  626. fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
  627. }
  628. return nil
  629. }
  630. func startMacApp(client *api.Client) error {
  631. exe, err := os.Executable()
  632. if err != nil {
  633. return err
  634. }
  635. link, err := os.Readlink(exe)
  636. if err != nil {
  637. return err
  638. }
  639. if !strings.Contains(link, "Ollama.app") {
  640. return fmt.Errorf("could not find ollama app")
  641. }
  642. path := strings.Split(link, "Ollama.app")
  643. if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
  644. return err
  645. }
  646. // wait for the server to start
  647. timeout := time.After(5 * time.Second)
  648. tick := time.Tick(500 * time.Millisecond)
  649. for {
  650. select {
  651. case <-timeout:
  652. return errors.New("timed out waiting for server to start")
  653. case <-tick:
  654. if err := client.Heartbeat(context.Background()); err == nil {
  655. return nil // server has started
  656. }
  657. }
  658. }
  659. }
  660. func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
  661. client, err := api.FromEnv()
  662. if err != nil {
  663. return err
  664. }
  665. if err := client.Heartbeat(context.Background()); err != nil {
  666. if !strings.Contains(err.Error(), "connection refused") {
  667. return err
  668. }
  669. if runtime.GOOS == "darwin" {
  670. if err := startMacApp(client); err != nil {
  671. return fmt.Errorf("could not connect to ollama app, is it running?")
  672. }
  673. } else {
  674. return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
  675. }
  676. }
  677. return nil
  678. }
  679. func NewCLI() *cobra.Command {
  680. log.SetFlags(log.LstdFlags | log.Lshortfile)
  681. rootCmd := &cobra.Command{
  682. Use: "ollama",
  683. Short: "Large language model runner",
  684. SilenceUsage: true,
  685. SilenceErrors: true,
  686. CompletionOptions: cobra.CompletionOptions{
  687. DisableDefaultCmd: true,
  688. },
  689. Version: version.Version,
  690. }
  691. cobra.EnableCommandSorting = false
  692. createCmd := &cobra.Command{
  693. Use: "create MODEL",
  694. Short: "Create a model from a Modelfile",
  695. Args: cobra.MinimumNArgs(1),
  696. PreRunE: checkServerHeartbeat,
  697. RunE: CreateHandler,
  698. }
  699. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  700. showCmd := &cobra.Command{
  701. Use: "show MODEL",
  702. Short: "Show information for a model",
  703. Args: cobra.MinimumNArgs(1),
  704. PreRunE: checkServerHeartbeat,
  705. RunE: ShowHandler,
  706. }
  707. showCmd.Flags().Bool("license", false, "Show license of a model")
  708. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  709. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  710. showCmd.Flags().Bool("template", false, "Show template of a model")
  711. showCmd.Flags().Bool("system", false, "Show system prompt of a model")
  712. runCmd := &cobra.Command{
  713. Use: "run MODEL [PROMPT]",
  714. Short: "Run a model",
  715. Args: cobra.MinimumNArgs(1),
  716. PreRunE: checkServerHeartbeat,
  717. RunE: RunHandler,
  718. }
  719. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  720. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  721. serveCmd := &cobra.Command{
  722. Use: "serve",
  723. Aliases: []string{"start"},
  724. Short: "Start ollama",
  725. RunE: RunServer,
  726. }
  727. pullCmd := &cobra.Command{
  728. Use: "pull MODEL",
  729. Short: "Pull a model from a registry",
  730. Args: cobra.MinimumNArgs(1),
  731. PreRunE: checkServerHeartbeat,
  732. RunE: PullHandler,
  733. }
  734. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  735. pushCmd := &cobra.Command{
  736. Use: "push MODEL",
  737. Short: "Push a model to a registry",
  738. Args: cobra.MinimumNArgs(1),
  739. PreRunE: checkServerHeartbeat,
  740. RunE: PushHandler,
  741. }
  742. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  743. listCmd := &cobra.Command{
  744. Use: "list",
  745. Aliases: []string{"ls"},
  746. Short: "List models",
  747. PreRunE: checkServerHeartbeat,
  748. RunE: ListHandler,
  749. }
  750. copyCmd := &cobra.Command{
  751. Use: "cp",
  752. Short: "Copy a model",
  753. Args: cobra.MinimumNArgs(2),
  754. PreRunE: checkServerHeartbeat,
  755. RunE: CopyHandler,
  756. }
  757. deleteCmd := &cobra.Command{
  758. Use: "rm",
  759. Short: "Remove a model",
  760. Args: cobra.MinimumNArgs(1),
  761. PreRunE: checkServerHeartbeat,
  762. RunE: DeleteHandler,
  763. }
  764. rootCmd.AddCommand(
  765. serveCmd,
  766. createCmd,
  767. showCmd,
  768. runCmd,
  769. pullCmd,
  770. pushCmd,
  771. listCmd,
  772. copyCmd,
  773. deleteCmd,
  774. )
  775. return rootCmd
  776. }