cmd.go 34 KB

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