cmd.go 35 KB

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