cmd.go 20 KB

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