cmd.go 35 KB

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