cmd.go 21 KB

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