cmd.go 34 KB

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