cmd.go 35 KB

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