cmd.go 24 KB

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