cmd.go 23 KB

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