cmd.go 35 KB

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