cmd.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  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. insecure, err := cmd.Flags().GetBool("insecure")
  298. if err != nil {
  299. return err
  300. }
  301. client, err := api.ClientFromEnvironment()
  302. if err != nil {
  303. return err
  304. }
  305. p := progress.NewProgress(os.Stderr)
  306. defer p.Stop()
  307. bars := make(map[string]*progress.Bar)
  308. var status string
  309. var spinner *progress.Spinner
  310. fn := func(resp api.ProgressResponse) error {
  311. if resp.Digest != "" {
  312. if spinner != nil {
  313. spinner.Stop()
  314. }
  315. bar, ok := bars[resp.Digest]
  316. if !ok {
  317. bar = progress.NewBar(fmt.Sprintf("pulling %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  318. bars[resp.Digest] = bar
  319. p.Add(resp.Digest, bar)
  320. }
  321. bar.Set(resp.Completed)
  322. } else if status != resp.Status {
  323. if spinner != nil {
  324. spinner.Stop()
  325. }
  326. status = resp.Status
  327. spinner = progress.NewSpinner(status)
  328. p.Add(status, spinner)
  329. }
  330. return nil
  331. }
  332. request := api.PullRequest{Name: args[0], Insecure: insecure}
  333. if err := client.Pull(cmd.Context(), &request, fn); err != nil {
  334. return err
  335. }
  336. return nil
  337. }
  338. func RunGenerate(cmd *cobra.Command, args []string) error {
  339. interactive := true
  340. opts := runOptions{
  341. Model: args[0],
  342. WordWrap: os.Getenv("TERM") == "xterm-256color",
  343. Options: map[string]interface{}{},
  344. }
  345. format, err := cmd.Flags().GetString("format")
  346. if err != nil {
  347. return err
  348. }
  349. opts.Format = format
  350. prompts := args[1:]
  351. // prepend stdin to the prompt if provided
  352. if !term.IsTerminal(int(os.Stdin.Fd())) {
  353. in, err := io.ReadAll(os.Stdin)
  354. if err != nil {
  355. return err
  356. }
  357. prompts = append([]string{string(in)}, prompts...)
  358. opts.WordWrap = false
  359. interactive = false
  360. }
  361. opts.Prompt = strings.Join(prompts, " ")
  362. if len(prompts) > 0 {
  363. interactive = false
  364. }
  365. nowrap, err := cmd.Flags().GetBool("nowordwrap")
  366. if err != nil {
  367. return err
  368. }
  369. opts.WordWrap = !nowrap
  370. if !interactive {
  371. return generate(cmd, opts)
  372. }
  373. return generateInteractive(cmd, opts)
  374. }
  375. type generateContextKey string
  376. type runOptions struct {
  377. Model string
  378. ParentModel string
  379. Prompt string
  380. Messages []api.Message
  381. WordWrap bool
  382. Format string
  383. System string
  384. Template string
  385. Images []api.ImageData
  386. Options map[string]interface{}
  387. MultiModal bool
  388. }
  389. type displayResponseState struct {
  390. lineLength int
  391. wordBuffer string
  392. }
  393. func displayResponse(content string, wordWrap bool, state *displayResponseState) {
  394. termWidth, _, _ := term.GetSize(int(os.Stdout.Fd()))
  395. if wordWrap && termWidth >= 10 {
  396. for _, ch := range content {
  397. if state.lineLength+1 > termWidth-5 {
  398. if len(state.wordBuffer) > termWidth-10 {
  399. fmt.Printf("%s%c", state.wordBuffer, ch)
  400. state.wordBuffer = ""
  401. state.lineLength = 0
  402. continue
  403. }
  404. // backtrack the length of the last word and clear to the end of the line
  405. fmt.Printf("\x1b[%dD\x1b[K\n", len(state.wordBuffer))
  406. fmt.Printf("%s%c", state.wordBuffer, ch)
  407. state.lineLength = len(state.wordBuffer) + 1
  408. } else {
  409. fmt.Print(string(ch))
  410. state.lineLength += 1
  411. switch ch {
  412. case ' ':
  413. state.wordBuffer = ""
  414. case '\n':
  415. state.lineLength = 0
  416. default:
  417. state.wordBuffer += string(ch)
  418. }
  419. }
  420. }
  421. } else {
  422. fmt.Printf("%s%s", state.wordBuffer, content)
  423. if len(state.wordBuffer) > 0 {
  424. state.wordBuffer = ""
  425. }
  426. }
  427. }
  428. func chat(cmd *cobra.Command, opts runOptions) (*api.Message, error) {
  429. client, err := api.ClientFromEnvironment()
  430. if err != nil {
  431. return nil, err
  432. }
  433. p := progress.NewProgress(os.Stderr)
  434. defer p.StopAndClear()
  435. spinner := progress.NewSpinner("")
  436. p.Add("", spinner)
  437. cancelCtx, cancel := context.WithCancel(cmd.Context())
  438. defer cancel()
  439. sigChan := make(chan os.Signal, 1)
  440. signal.Notify(sigChan, syscall.SIGINT)
  441. go func() {
  442. <-sigChan
  443. cancel()
  444. }()
  445. var state *displayResponseState = &displayResponseState{}
  446. var latest api.ChatResponse
  447. var fullResponse strings.Builder
  448. var role string
  449. fn := func(response api.ChatResponse) error {
  450. p.StopAndClear()
  451. latest = response
  452. role = response.Message.Role
  453. content := response.Message.Content
  454. fullResponse.WriteString(content)
  455. displayResponse(content, opts.WordWrap, state)
  456. return nil
  457. }
  458. req := &api.ChatRequest{
  459. Model: opts.Model,
  460. Messages: opts.Messages,
  461. Format: opts.Format,
  462. Options: opts.Options,
  463. }
  464. if err := client.Chat(cancelCtx, req, fn); err != nil {
  465. if errors.Is(err, context.Canceled) {
  466. return nil, nil
  467. }
  468. return nil, err
  469. }
  470. if len(opts.Messages) > 0 {
  471. fmt.Println()
  472. fmt.Println()
  473. }
  474. verbose, err := cmd.Flags().GetBool("verbose")
  475. if err != nil {
  476. return nil, err
  477. }
  478. if verbose {
  479. latest.Summary()
  480. }
  481. return &api.Message{Role: role, Content: fullResponse.String()}, nil
  482. }
  483. func generate(cmd *cobra.Command, opts runOptions) error {
  484. client, err := api.ClientFromEnvironment()
  485. if err != nil {
  486. return err
  487. }
  488. p := progress.NewProgress(os.Stderr)
  489. defer p.StopAndClear()
  490. spinner := progress.NewSpinner("")
  491. p.Add("", spinner)
  492. var latest api.GenerateResponse
  493. generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
  494. if !ok {
  495. generateContext = []int{}
  496. }
  497. ctx, cancel := context.WithCancel(cmd.Context())
  498. defer cancel()
  499. sigChan := make(chan os.Signal, 1)
  500. signal.Notify(sigChan, syscall.SIGINT)
  501. go func() {
  502. <-sigChan
  503. cancel()
  504. }()
  505. var state *displayResponseState = &displayResponseState{}
  506. fn := func(response api.GenerateResponse) error {
  507. p.StopAndClear()
  508. latest = response
  509. content := response.Response
  510. displayResponse(content, opts.WordWrap, state)
  511. return nil
  512. }
  513. request := api.GenerateRequest{
  514. Model: opts.Model,
  515. Prompt: opts.Prompt,
  516. Context: generateContext,
  517. Format: opts.Format,
  518. System: opts.System,
  519. Template: opts.Template,
  520. Options: opts.Options,
  521. }
  522. if err := client.Generate(ctx, &request, fn); err != nil {
  523. if errors.Is(err, context.Canceled) {
  524. return nil
  525. }
  526. return err
  527. }
  528. if opts.Prompt != "" {
  529. fmt.Println()
  530. fmt.Println()
  531. }
  532. if !latest.Done {
  533. return nil
  534. }
  535. verbose, err := cmd.Flags().GetBool("verbose")
  536. if err != nil {
  537. return err
  538. }
  539. if verbose {
  540. latest.Summary()
  541. }
  542. ctx = context.WithValue(cmd.Context(), generateContextKey("context"), latest.Context)
  543. cmd.SetContext(ctx)
  544. return nil
  545. }
  546. func RunServer(cmd *cobra.Command, _ []string) error {
  547. host, port, err := net.SplitHostPort(os.Getenv("OLLAMA_HOST"))
  548. if err != nil {
  549. host, port = "127.0.0.1", "11434"
  550. if ip := net.ParseIP(strings.Trim(os.Getenv("OLLAMA_HOST"), "[]")); ip != nil {
  551. host = ip.String()
  552. }
  553. }
  554. if err := initializeKeypair(); err != nil {
  555. return err
  556. }
  557. ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
  558. if err != nil {
  559. return err
  560. }
  561. return server.Serve(ln)
  562. }
  563. func initializeKeypair() error {
  564. home, err := os.UserHomeDir()
  565. if err != nil {
  566. return err
  567. }
  568. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  569. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  570. _, err = os.Stat(privKeyPath)
  571. if os.IsNotExist(err) {
  572. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  573. _, privKey, err := ed25519.GenerateKey(rand.Reader)
  574. if err != nil {
  575. return err
  576. }
  577. privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
  578. if err != nil {
  579. return err
  580. }
  581. err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
  582. if err != nil {
  583. return fmt.Errorf("could not create directory %w", err)
  584. }
  585. err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
  586. if err != nil {
  587. return err
  588. }
  589. sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
  590. if err != nil {
  591. return err
  592. }
  593. pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())
  594. err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
  595. if err != nil {
  596. return err
  597. }
  598. fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
  599. }
  600. return nil
  601. }
  602. func startMacApp(ctx context.Context, client *api.Client) error {
  603. exe, err := os.Executable()
  604. if err != nil {
  605. return err
  606. }
  607. link, err := os.Readlink(exe)
  608. if err != nil {
  609. return err
  610. }
  611. if !strings.Contains(link, "Ollama.app") {
  612. return fmt.Errorf("could not find ollama app")
  613. }
  614. path := strings.Split(link, "Ollama.app")
  615. if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
  616. return err
  617. }
  618. // wait for the server to start
  619. timeout := time.After(5 * time.Second)
  620. tick := time.Tick(500 * time.Millisecond)
  621. for {
  622. select {
  623. case <-timeout:
  624. return errors.New("timed out waiting for server to start")
  625. case <-tick:
  626. if err := client.Heartbeat(ctx); err == nil {
  627. return nil // server has started
  628. }
  629. }
  630. }
  631. }
  632. func checkServerHeartbeat(cmd *cobra.Command, _ []string) error {
  633. client, err := api.ClientFromEnvironment()
  634. if err != nil {
  635. return err
  636. }
  637. if err := client.Heartbeat(cmd.Context()); err != nil {
  638. if !strings.Contains(err.Error(), "connection refused") {
  639. return err
  640. }
  641. if runtime.GOOS == "darwin" {
  642. if err := startMacApp(cmd.Context(), client); err != nil {
  643. return fmt.Errorf("could not connect to ollama app, is it running?")
  644. }
  645. } else {
  646. return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
  647. }
  648. }
  649. return nil
  650. }
  651. func versionHandler(cmd *cobra.Command, _ []string) {
  652. client, err := api.ClientFromEnvironment()
  653. if err != nil {
  654. return
  655. }
  656. serverVersion, err := client.Version(cmd.Context())
  657. if err != nil {
  658. fmt.Println("Warning: could not connect to a running Ollama instance")
  659. }
  660. if serverVersion != "" {
  661. fmt.Printf("ollama version is %s\n", serverVersion)
  662. }
  663. if serverVersion != version.Version {
  664. fmt.Printf("Warning: client version is %s\n", version.Version)
  665. }
  666. }
  667. func NewCLI() *cobra.Command {
  668. log.SetFlags(log.LstdFlags | log.Lshortfile)
  669. cobra.EnableCommandSorting = false
  670. rootCmd := &cobra.Command{
  671. Use: "ollama",
  672. Short: "Large language model runner",
  673. SilenceUsage: true,
  674. SilenceErrors: true,
  675. CompletionOptions: cobra.CompletionOptions{
  676. DisableDefaultCmd: true,
  677. },
  678. Run: func(cmd *cobra.Command, args []string) {
  679. if version, _ := cmd.Flags().GetBool("version"); version {
  680. versionHandler(cmd, args)
  681. return
  682. }
  683. cmd.Print(cmd.UsageString())
  684. },
  685. }
  686. rootCmd.Flags().BoolP("version", "v", false, "Show version information")
  687. createCmd := &cobra.Command{
  688. Use: "create MODEL",
  689. Short: "Create a model from a Modelfile",
  690. Args: cobra.ExactArgs(1),
  691. PreRunE: checkServerHeartbeat,
  692. RunE: CreateHandler,
  693. }
  694. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  695. showCmd := &cobra.Command{
  696. Use: "show MODEL",
  697. Short: "Show information for a model",
  698. Args: cobra.ExactArgs(1),
  699. PreRunE: checkServerHeartbeat,
  700. RunE: ShowHandler,
  701. }
  702. showCmd.Flags().Bool("license", false, "Show license of a model")
  703. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  704. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  705. showCmd.Flags().Bool("template", false, "Show template of a model")
  706. showCmd.Flags().Bool("system", false, "Show system message of a model")
  707. runCmd := &cobra.Command{
  708. Use: "run MODEL [PROMPT]",
  709. Short: "Run a model",
  710. Args: cobra.MinimumNArgs(1),
  711. PreRunE: checkServerHeartbeat,
  712. RunE: RunHandler,
  713. }
  714. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  715. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  716. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  717. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  718. serveCmd := &cobra.Command{
  719. Use: "serve",
  720. Aliases: []string{"start"},
  721. Short: "Start ollama",
  722. Args: cobra.ExactArgs(0),
  723. RunE: RunServer,
  724. }
  725. pullCmd := &cobra.Command{
  726. Use: "pull MODEL",
  727. Short: "Pull a model from a registry",
  728. Args: cobra.ExactArgs(1),
  729. PreRunE: checkServerHeartbeat,
  730. RunE: PullHandler,
  731. }
  732. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  733. pushCmd := &cobra.Command{
  734. Use: "push MODEL",
  735. Short: "Push a model to a registry",
  736. Args: cobra.ExactArgs(1),
  737. PreRunE: checkServerHeartbeat,
  738. RunE: PushHandler,
  739. }
  740. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  741. listCmd := &cobra.Command{
  742. Use: "list",
  743. Aliases: []string{"ls"},
  744. Short: "List models",
  745. PreRunE: checkServerHeartbeat,
  746. RunE: ListHandler,
  747. }
  748. copyCmd := &cobra.Command{
  749. Use: "cp SOURCE TARGET",
  750. Short: "Copy a model",
  751. Args: cobra.ExactArgs(2),
  752. PreRunE: checkServerHeartbeat,
  753. RunE: CopyHandler,
  754. }
  755. deleteCmd := &cobra.Command{
  756. Use: "rm MODEL [MODEL...]",
  757. Short: "Remove a model",
  758. Args: cobra.MinimumNArgs(1),
  759. PreRunE: checkServerHeartbeat,
  760. RunE: DeleteHandler,
  761. }
  762. rootCmd.AddCommand(
  763. serveCmd,
  764. createCmd,
  765. showCmd,
  766. runCmd,
  767. pullCmd,
  768. pushCmd,
  769. listCmd,
  770. copyCmd,
  771. deleteCmd,
  772. )
  773. return rootCmd
  774. }