cmd.go 35 KB

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