cmd.go 23 KB

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