cmd.go 33 KB

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