cmd.go 32 KB

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