cmd.go 35 KB

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