cmd.go 34 KB

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