cmd.go 21 KB

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