cmd.go 35 KB

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