cmd.go 34 KB

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