cmd.go 26 KB

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