cmd.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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/exp/slices"
  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. show, 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. interactive := true
  136. opts := runOptions{
  137. Model: args[0],
  138. WordWrap: os.Getenv("TERM") == "xterm-256color",
  139. Options: map[string]interface{}{},
  140. MultiModal: slices.Contains(show.Details.Families, "clip"),
  141. ParentModel: show.Details.ParentModel,
  142. }
  143. format, err := cmd.Flags().GetString("format")
  144. if err != nil {
  145. return err
  146. }
  147. opts.Format = format
  148. prompts := args[1:]
  149. // prepend stdin to the prompt if provided
  150. if !term.IsTerminal(int(os.Stdin.Fd())) {
  151. in, err := io.ReadAll(os.Stdin)
  152. if err != nil {
  153. return err
  154. }
  155. prompts = append([]string{string(in)}, prompts...)
  156. opts.WordWrap = false
  157. interactive = false
  158. }
  159. opts.Prompt = strings.Join(prompts, " ")
  160. if len(prompts) > 0 {
  161. interactive = false
  162. }
  163. nowrap, err := cmd.Flags().GetBool("nowordwrap")
  164. if err != nil {
  165. return err
  166. }
  167. opts.WordWrap = !nowrap
  168. if !interactive {
  169. return generate(cmd, opts)
  170. }
  171. return generateInteractive(cmd, opts)
  172. }
  173. func PushHandler(cmd *cobra.Command, args []string) error {
  174. client, err := api.ClientFromEnvironment()
  175. if err != nil {
  176. return err
  177. }
  178. insecure, err := cmd.Flags().GetBool("insecure")
  179. if err != nil {
  180. return err
  181. }
  182. p := progress.NewProgress(os.Stderr)
  183. defer p.Stop()
  184. bars := make(map[string]*progress.Bar)
  185. var status string
  186. var spinner *progress.Spinner
  187. fn := func(resp api.ProgressResponse) error {
  188. if resp.Digest != "" {
  189. if spinner != nil {
  190. spinner.Stop()
  191. }
  192. bar, ok := bars[resp.Digest]
  193. if !ok {
  194. bar = progress.NewBar(fmt.Sprintf("pushing %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  195. bars[resp.Digest] = bar
  196. p.Add(resp.Digest, bar)
  197. }
  198. bar.Set(resp.Completed)
  199. } else if status != resp.Status {
  200. if spinner != nil {
  201. spinner.Stop()
  202. }
  203. status = resp.Status
  204. spinner = progress.NewSpinner(status)
  205. p.Add(status, spinner)
  206. }
  207. return nil
  208. }
  209. request := api.PushRequest{Name: args[0], Insecure: insecure}
  210. if err := client.Push(cmd.Context(), &request, fn); err != nil {
  211. return err
  212. }
  213. spinner.Stop()
  214. return nil
  215. }
  216. func ListHandler(cmd *cobra.Command, args []string) error {
  217. client, err := api.ClientFromEnvironment()
  218. if err != nil {
  219. return err
  220. }
  221. models, err := client.List(cmd.Context())
  222. if err != nil {
  223. return err
  224. }
  225. var data [][]string
  226. for _, m := range models.Models {
  227. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  228. data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), format.HumanTime(m.ModifiedAt, "Never")})
  229. }
  230. }
  231. table := tablewriter.NewWriter(os.Stdout)
  232. table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
  233. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  234. table.SetAlignment(tablewriter.ALIGN_LEFT)
  235. table.SetHeaderLine(false)
  236. table.SetBorder(false)
  237. table.SetNoWhiteSpace(true)
  238. table.SetTablePadding("\t")
  239. table.AppendBulk(data)
  240. table.Render()
  241. return nil
  242. }
  243. func DeleteHandler(cmd *cobra.Command, args []string) error {
  244. client, err := api.ClientFromEnvironment()
  245. if err != nil {
  246. return err
  247. }
  248. for _, name := range args {
  249. req := api.DeleteRequest{Name: name}
  250. if err := client.Delete(cmd.Context(), &req); err != nil {
  251. return err
  252. }
  253. fmt.Printf("deleted '%s'\n", name)
  254. }
  255. return nil
  256. }
  257. func ShowHandler(cmd *cobra.Command, args []string) error {
  258. client, err := api.ClientFromEnvironment()
  259. if err != nil {
  260. return err
  261. }
  262. if len(args) != 1 {
  263. return errors.New("missing model name")
  264. }
  265. license, errLicense := cmd.Flags().GetBool("license")
  266. modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
  267. parameters, errParams := cmd.Flags().GetBool("parameters")
  268. system, errSystem := cmd.Flags().GetBool("system")
  269. template, errTemplate := cmd.Flags().GetBool("template")
  270. for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
  271. if boolErr != nil {
  272. return errors.New("error retrieving flags")
  273. }
  274. }
  275. flagsSet := 0
  276. showType := ""
  277. if license {
  278. flagsSet++
  279. showType = "license"
  280. }
  281. if modelfile {
  282. flagsSet++
  283. showType = "modelfile"
  284. }
  285. if parameters {
  286. flagsSet++
  287. showType = "parameters"
  288. }
  289. if system {
  290. flagsSet++
  291. showType = "system"
  292. }
  293. if template {
  294. flagsSet++
  295. showType = "template"
  296. }
  297. if flagsSet > 1 {
  298. return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
  299. } else if flagsSet == 0 {
  300. return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
  301. }
  302. req := api.ShowRequest{Name: args[0]}
  303. resp, err := client.Show(cmd.Context(), &req)
  304. if err != nil {
  305. return err
  306. }
  307. switch showType {
  308. case "license":
  309. fmt.Println(resp.License)
  310. case "modelfile":
  311. fmt.Println(resp.Modelfile)
  312. case "parameters":
  313. fmt.Println(resp.Parameters)
  314. case "system":
  315. fmt.Println(resp.System)
  316. case "template":
  317. fmt.Println(resp.Template)
  318. }
  319. return nil
  320. }
  321. func CopyHandler(cmd *cobra.Command, args []string) error {
  322. client, err := api.ClientFromEnvironment()
  323. if err != nil {
  324. return err
  325. }
  326. req := api.CopyRequest{Source: args[0], Destination: args[1]}
  327. if err := client.Copy(cmd.Context(), &req); err != nil {
  328. return err
  329. }
  330. fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
  331. return nil
  332. }
  333. func PullHandler(cmd *cobra.Command, args []string) error {
  334. insecure, err := cmd.Flags().GetBool("insecure")
  335. if err != nil {
  336. return err
  337. }
  338. client, err := api.ClientFromEnvironment()
  339. if err != nil {
  340. return err
  341. }
  342. p := progress.NewProgress(os.Stderr)
  343. defer p.Stop()
  344. bars := make(map[string]*progress.Bar)
  345. var status string
  346. var spinner *progress.Spinner
  347. fn := func(resp api.ProgressResponse) error {
  348. if resp.Digest != "" {
  349. if spinner != nil {
  350. spinner.Stop()
  351. }
  352. bar, ok := bars[resp.Digest]
  353. if !ok {
  354. bar = progress.NewBar(fmt.Sprintf("pulling %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  355. bars[resp.Digest] = bar
  356. p.Add(resp.Digest, bar)
  357. }
  358. bar.Set(resp.Completed)
  359. } else if status != resp.Status {
  360. if spinner != nil {
  361. spinner.Stop()
  362. }
  363. status = resp.Status
  364. spinner = progress.NewSpinner(status)
  365. p.Add(status, spinner)
  366. }
  367. return nil
  368. }
  369. request := api.PullRequest{Name: args[0], Insecure: insecure}
  370. if err := client.Pull(cmd.Context(), &request, fn); err != nil {
  371. return err
  372. }
  373. return nil
  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. if opts.MultiModal {
  514. opts.Prompt, opts.Images, err = extractFileData(opts.Prompt)
  515. if err != nil {
  516. return err
  517. }
  518. }
  519. request := api.GenerateRequest{
  520. Model: opts.Model,
  521. Prompt: opts.Prompt,
  522. Context: generateContext,
  523. Images: opts.Images,
  524. Format: opts.Format,
  525. System: opts.System,
  526. Template: opts.Template,
  527. Options: opts.Options,
  528. }
  529. if err := client.Generate(ctx, &request, fn); err != nil {
  530. if errors.Is(err, context.Canceled) {
  531. return nil
  532. }
  533. return err
  534. }
  535. if opts.Prompt != "" {
  536. fmt.Println()
  537. fmt.Println()
  538. }
  539. if !latest.Done {
  540. return nil
  541. }
  542. verbose, err := cmd.Flags().GetBool("verbose")
  543. if err != nil {
  544. return err
  545. }
  546. if verbose {
  547. latest.Summary()
  548. }
  549. ctx = context.WithValue(cmd.Context(), generateContextKey("context"), latest.Context)
  550. cmd.SetContext(ctx)
  551. return nil
  552. }
  553. func RunServer(cmd *cobra.Command, _ []string) error {
  554. host, port, err := net.SplitHostPort(os.Getenv("OLLAMA_HOST"))
  555. if err != nil {
  556. host, port = "127.0.0.1", "11434"
  557. if ip := net.ParseIP(strings.Trim(os.Getenv("OLLAMA_HOST"), "[]")); ip != nil {
  558. host = ip.String()
  559. }
  560. }
  561. if err := initializeKeypair(); err != nil {
  562. return err
  563. }
  564. ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
  565. if err != nil {
  566. return err
  567. }
  568. return server.Serve(ln)
  569. }
  570. func initializeKeypair() error {
  571. home, err := os.UserHomeDir()
  572. if err != nil {
  573. return err
  574. }
  575. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  576. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  577. _, err = os.Stat(privKeyPath)
  578. if os.IsNotExist(err) {
  579. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  580. _, privKey, err := ed25519.GenerateKey(rand.Reader)
  581. if err != nil {
  582. return err
  583. }
  584. privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
  585. if err != nil {
  586. return err
  587. }
  588. err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
  589. if err != nil {
  590. return fmt.Errorf("could not create directory %w", err)
  591. }
  592. err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
  593. if err != nil {
  594. return err
  595. }
  596. sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
  597. if err != nil {
  598. return err
  599. }
  600. pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())
  601. err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
  602. if err != nil {
  603. return err
  604. }
  605. fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
  606. }
  607. return nil
  608. }
  609. func startMacApp(ctx context.Context, client *api.Client) error {
  610. exe, err := os.Executable()
  611. if err != nil {
  612. return err
  613. }
  614. link, err := os.Readlink(exe)
  615. if err != nil {
  616. return err
  617. }
  618. if !strings.Contains(link, "Ollama.app") {
  619. return fmt.Errorf("could not find ollama app")
  620. }
  621. path := strings.Split(link, "Ollama.app")
  622. if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
  623. return err
  624. }
  625. // wait for the server to start
  626. timeout := time.After(5 * time.Second)
  627. tick := time.Tick(500 * time.Millisecond)
  628. for {
  629. select {
  630. case <-timeout:
  631. return errors.New("timed out waiting for server to start")
  632. case <-tick:
  633. if err := client.Heartbeat(ctx); err == nil {
  634. return nil // server has started
  635. }
  636. }
  637. }
  638. }
  639. func checkServerHeartbeat(cmd *cobra.Command, _ []string) error {
  640. client, err := api.ClientFromEnvironment()
  641. if err != nil {
  642. return err
  643. }
  644. if err := client.Heartbeat(cmd.Context()); err != nil {
  645. if !strings.Contains(err.Error(), "connection refused") {
  646. return err
  647. }
  648. if runtime.GOOS == "darwin" {
  649. if err := startMacApp(cmd.Context(), client); err != nil {
  650. return fmt.Errorf("could not connect to ollama app, is it running?")
  651. }
  652. } else {
  653. return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
  654. }
  655. }
  656. return nil
  657. }
  658. func versionHandler(cmd *cobra.Command, _ []string) {
  659. client, err := api.ClientFromEnvironment()
  660. if err != nil {
  661. return
  662. }
  663. serverVersion, err := client.Version(cmd.Context())
  664. if err != nil {
  665. fmt.Println("Warning: could not connect to a running Ollama instance")
  666. }
  667. if serverVersion != "" {
  668. fmt.Printf("ollama version is %s\n", serverVersion)
  669. }
  670. if serverVersion != version.Version {
  671. fmt.Printf("Warning: client version is %s\n", version.Version)
  672. }
  673. }
  674. func NewCLI() *cobra.Command {
  675. log.SetFlags(log.LstdFlags | log.Lshortfile)
  676. cobra.EnableCommandSorting = false
  677. rootCmd := &cobra.Command{
  678. Use: "ollama",
  679. Short: "Large language model runner",
  680. SilenceUsage: true,
  681. SilenceErrors: true,
  682. CompletionOptions: cobra.CompletionOptions{
  683. DisableDefaultCmd: true,
  684. },
  685. Run: func(cmd *cobra.Command, args []string) {
  686. if version, _ := cmd.Flags().GetBool("version"); version {
  687. versionHandler(cmd, args)
  688. return
  689. }
  690. cmd.Print(cmd.UsageString())
  691. },
  692. }
  693. rootCmd.Flags().BoolP("version", "v", false, "Show version information")
  694. createCmd := &cobra.Command{
  695. Use: "create MODEL",
  696. Short: "Create a model from a Modelfile",
  697. Args: cobra.ExactArgs(1),
  698. PreRunE: checkServerHeartbeat,
  699. RunE: CreateHandler,
  700. }
  701. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  702. showCmd := &cobra.Command{
  703. Use: "show MODEL",
  704. Short: "Show information for a model",
  705. Args: cobra.ExactArgs(1),
  706. PreRunE: checkServerHeartbeat,
  707. RunE: ShowHandler,
  708. }
  709. showCmd.Flags().Bool("license", false, "Show license of a model")
  710. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  711. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  712. showCmd.Flags().Bool("template", false, "Show template of a model")
  713. showCmd.Flags().Bool("system", false, "Show system message of a model")
  714. runCmd := &cobra.Command{
  715. Use: "run MODEL [PROMPT]",
  716. Short: "Run a model",
  717. Args: cobra.MinimumNArgs(1),
  718. PreRunE: checkServerHeartbeat,
  719. RunE: RunHandler,
  720. }
  721. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  722. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  723. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  724. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  725. serveCmd := &cobra.Command{
  726. Use: "serve",
  727. Aliases: []string{"start"},
  728. Short: "Start ollama",
  729. Args: cobra.ExactArgs(0),
  730. RunE: RunServer,
  731. }
  732. pullCmd := &cobra.Command{
  733. Use: "pull MODEL",
  734. Short: "Pull a model from a registry",
  735. Args: cobra.ExactArgs(1),
  736. PreRunE: checkServerHeartbeat,
  737. RunE: PullHandler,
  738. }
  739. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  740. pushCmd := &cobra.Command{
  741. Use: "push MODEL",
  742. Short: "Push a model to a registry",
  743. Args: cobra.ExactArgs(1),
  744. PreRunE: checkServerHeartbeat,
  745. RunE: PushHandler,
  746. }
  747. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  748. listCmd := &cobra.Command{
  749. Use: "list",
  750. Aliases: []string{"ls"},
  751. Short: "List models",
  752. PreRunE: checkServerHeartbeat,
  753. RunE: ListHandler,
  754. }
  755. copyCmd := &cobra.Command{
  756. Use: "cp SOURCE TARGET",
  757. Short: "Copy a model",
  758. Args: cobra.ExactArgs(2),
  759. PreRunE: checkServerHeartbeat,
  760. RunE: CopyHandler,
  761. }
  762. deleteCmd := &cobra.Command{
  763. Use: "rm MODEL [MODEL...]",
  764. Short: "Remove a model",
  765. Args: cobra.MinimumNArgs(1),
  766. PreRunE: checkServerHeartbeat,
  767. RunE: DeleteHandler,
  768. }
  769. rootCmd.AddCommand(
  770. serveCmd,
  771. createCmd,
  772. showCmd,
  773. runCmd,
  774. pullCmd,
  775. pushCmd,
  776. listCmd,
  777. copyCmd,
  778. deleteCmd,
  779. )
  780. return rootCmd
  781. }