cmd.go 23 KB

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