cmd.go 29 KB

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