cmd.go 35 KB

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