cmd.go 21 KB

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