cmd.go 23 KB

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