cmd.go 32 KB

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