cmd.go 33 KB

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