cmd.go 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  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. "net"
  15. "net/http"
  16. "os"
  17. "os/signal"
  18. "path/filepath"
  19. "regexp"
  20. "runtime"
  21. "strings"
  22. "syscall"
  23. "time"
  24. "github.com/containerd/console"
  25. "github.com/olekukonko/tablewriter"
  26. "github.com/spf13/cobra"
  27. "golang.org/x/crypto/ssh"
  28. "golang.org/x/exp/slices"
  29. "golang.org/x/term"
  30. "github.com/ollama/ollama/api"
  31. "github.com/ollama/ollama/auth"
  32. "github.com/ollama/ollama/format"
  33. "github.com/ollama/ollama/parser"
  34. "github.com/ollama/ollama/progress"
  35. "github.com/ollama/ollama/server"
  36. "github.com/ollama/ollama/types/errtypes"
  37. "github.com/ollama/ollama/types/model"
  38. "github.com/ollama/ollama/version"
  39. )
  40. func CreateHandler(cmd *cobra.Command, args []string) error {
  41. filename, _ := cmd.Flags().GetString("file")
  42. filename, err := filepath.Abs(filename)
  43. if err != nil {
  44. return err
  45. }
  46. client, err := api.ClientFromEnvironment()
  47. if err != nil {
  48. return err
  49. }
  50. p := progress.NewProgress(os.Stderr)
  51. defer p.Stop()
  52. modelfile, err := os.ReadFile(filename)
  53. if err != nil {
  54. return err
  55. }
  56. commands, err := parser.Parse(bytes.NewReader(modelfile))
  57. if err != nil {
  58. return err
  59. }
  60. home, err := os.UserHomeDir()
  61. if err != nil {
  62. return err
  63. }
  64. status := "transferring model data"
  65. spinner := progress.NewSpinner(status)
  66. p.Add(status, spinner)
  67. for _, c := range commands {
  68. switch c.Name {
  69. case "model", "adapter":
  70. path := c.Args
  71. if path == "~" {
  72. path = home
  73. } else if strings.HasPrefix(path, "~/") {
  74. path = filepath.Join(home, path[2:])
  75. }
  76. if !filepath.IsAbs(path) {
  77. path = filepath.Join(filepath.Dir(filename), path)
  78. }
  79. fi, err := os.Stat(path)
  80. if errors.Is(err, os.ErrNotExist) && c.Name == "model" {
  81. continue
  82. } else if err != nil {
  83. return err
  84. }
  85. if fi.IsDir() {
  86. // this is likely a safetensors or pytorch directory
  87. // TODO make this work w/ adapters
  88. tempfile, err := tempZipFiles(path)
  89. if err != nil {
  90. return err
  91. }
  92. defer os.RemoveAll(tempfile)
  93. path = tempfile
  94. }
  95. digest, err := createBlob(cmd, client, path)
  96. if err != nil {
  97. return err
  98. }
  99. name := c.Name
  100. if c.Name == "model" {
  101. name = "from"
  102. }
  103. re := regexp.MustCompile(fmt.Sprintf(`(?im)^(%s)\s+%s\s*$`, name, c.Args))
  104. modelfile = re.ReplaceAll(modelfile, []byte("$1 @"+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. quantization, _ := cmd.Flags().GetString("quantization")
  127. request := api.CreateRequest{Name: args[0], Modelfile: string(modelfile), Quantization: quantization}
  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. zipfile := zip.NewWriter(tempfile)
  140. defer zipfile.Close()
  141. detectContentType := func(path string) (string, error) {
  142. f, err := os.Open(path)
  143. if err != nil {
  144. return "", err
  145. }
  146. defer f.Close()
  147. var b bytes.Buffer
  148. b.Grow(512)
  149. if _, err := io.CopyN(&b, f, 512); err != nil && !errors.Is(err, io.EOF) {
  150. return "", err
  151. }
  152. contentType, _, _ := strings.Cut(http.DetectContentType(b.Bytes()), ";")
  153. return contentType, nil
  154. }
  155. glob := func(pattern, contentType string) ([]string, error) {
  156. matches, err := filepath.Glob(pattern)
  157. if err != nil {
  158. return nil, err
  159. }
  160. for _, safetensor := range matches {
  161. if ct, err := detectContentType(safetensor); err != nil {
  162. return nil, err
  163. } else if ct != contentType {
  164. return nil, fmt.Errorf("invalid content type: expected %s for %s", ct, safetensor)
  165. }
  166. }
  167. return matches, nil
  168. }
  169. var files []string
  170. if st, _ := glob(filepath.Join(path, "model*.safetensors"), "application/octet-stream"); len(st) > 0 {
  171. // safetensors files might be unresolved git lfs references; skip if they are
  172. // covers model-x-of-y.safetensors, model.fp32-x-of-y.safetensors, model.safetensors
  173. files = append(files, st...)
  174. } else if pt, _ := glob(filepath.Join(path, "pytorch_model*.bin"), "application/zip"); len(pt) > 0 {
  175. // pytorch files might also be unresolved git lfs references; skip if they are
  176. // covers pytorch_model-x-of-y.bin, pytorch_model.fp32-x-of-y.bin, pytorch_model.bin
  177. files = append(files, pt...)
  178. } else if pt, _ := glob(filepath.Join(path, "consolidated*.pth"), "application/octet-stream"); len(pt) > 0 {
  179. // pytorch files might also be unresolved git lfs references; skip if they are
  180. // covers consolidated.x.pth, consolidated.pth
  181. files = append(files, pt...)
  182. } else {
  183. return "", errors.New("no safetensors or torch files found")
  184. }
  185. // add configuration files, json files are detected as text/plain
  186. js, err := glob(filepath.Join(path, "*.json"), "text/plain")
  187. if err != nil {
  188. return "", err
  189. }
  190. files = append(files, js...)
  191. if tks, _ := glob(filepath.Join(path, "tokenizer.model"), "application/octet-stream"); len(tks) > 0 {
  192. // add tokenizer.model if it exists, tokenizer.json is automatically picked up by the previous glob
  193. // tokenizer.model might be a unresolved git lfs reference; error if it is
  194. files = append(files, tks...)
  195. } else if tks, _ := glob(filepath.Join(path, "**/tokenizer.model"), "text/plain"); len(tks) > 0 {
  196. // some times tokenizer.model is in a subdirectory (e.g. meta-llama/Meta-Llama-3-8B)
  197. files = append(files, tks...)
  198. }
  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. client, err := api.ClientFromEnvironment()
  244. if err != nil {
  245. return err
  246. }
  247. name := args[0]
  248. // check if the model exists on the server
  249. show, err := client.Show(cmd.Context(), &api.ShowRequest{Name: name})
  250. var statusError api.StatusError
  251. switch {
  252. case errors.As(err, &statusError) && statusError.StatusCode == http.StatusNotFound:
  253. if err := PullHandler(cmd, []string{name}); err != nil {
  254. return err
  255. }
  256. show, err = client.Show(cmd.Context(), &api.ShowRequest{Name: name})
  257. if err != nil {
  258. return err
  259. }
  260. case err != nil:
  261. return err
  262. }
  263. interactive := true
  264. opts := runOptions{
  265. Model: args[0],
  266. WordWrap: os.Getenv("TERM") == "xterm-256color",
  267. Options: map[string]interface{}{},
  268. MultiModal: slices.Contains(show.Details.Families, "clip"),
  269. ParentModel: show.Details.ParentModel,
  270. }
  271. format, err := cmd.Flags().GetString("format")
  272. if err != nil {
  273. return err
  274. }
  275. opts.Format = format
  276. prompts := args[1:]
  277. // prepend stdin to the prompt if provided
  278. if !term.IsTerminal(int(os.Stdin.Fd())) {
  279. in, err := io.ReadAll(os.Stdin)
  280. if err != nil {
  281. return err
  282. }
  283. prompts = append([]string{string(in)}, prompts...)
  284. opts.WordWrap = false
  285. interactive = false
  286. }
  287. opts.Prompt = strings.Join(prompts, " ")
  288. if len(prompts) > 0 {
  289. interactive = false
  290. }
  291. nowrap, err := cmd.Flags().GetBool("nowordwrap")
  292. if err != nil {
  293. return err
  294. }
  295. opts.WordWrap = !nowrap
  296. if !interactive {
  297. return generate(cmd, opts)
  298. }
  299. return generateInteractive(cmd, opts)
  300. }
  301. func errFromUnknownKey(unknownKeyErr error) error {
  302. // find SSH public key in the error message
  303. sshKeyPattern := `ssh-\w+ [^\s"]+`
  304. re := regexp.MustCompile(sshKeyPattern)
  305. matches := re.FindStringSubmatch(unknownKeyErr.Error())
  306. if len(matches) > 0 {
  307. serverPubKey := matches[0]
  308. localPubKey, err := auth.GetPublicKey()
  309. if err != nil {
  310. return unknownKeyErr
  311. }
  312. if runtime.GOOS == "linux" && serverPubKey != localPubKey {
  313. // try the ollama service public key
  314. svcPubKey, err := os.ReadFile("/usr/share/ollama/.ollama/id_ed25519.pub")
  315. if err != nil {
  316. return unknownKeyErr
  317. }
  318. localPubKey = strings.TrimSpace(string(svcPubKey))
  319. }
  320. // check if the returned public key matches the local public key, this prevents adding a remote key to the user's account
  321. if serverPubKey != localPubKey {
  322. return unknownKeyErr
  323. }
  324. var msg strings.Builder
  325. msg.WriteString(unknownKeyErr.Error())
  326. msg.WriteString("\n\nYour ollama key is:\n")
  327. msg.WriteString(localPubKey)
  328. msg.WriteString("\nAdd your key at:\n")
  329. msg.WriteString("https://ollama.com/settings/keys")
  330. return errors.New(msg.String())
  331. }
  332. return unknownKeyErr
  333. }
  334. func PushHandler(cmd *cobra.Command, args []string) error {
  335. client, err := api.ClientFromEnvironment()
  336. if err != nil {
  337. return err
  338. }
  339. insecure, err := cmd.Flags().GetBool("insecure")
  340. if err != nil {
  341. return err
  342. }
  343. p := progress.NewProgress(os.Stderr)
  344. defer p.Stop()
  345. bars := make(map[string]*progress.Bar)
  346. var status string
  347. var spinner *progress.Spinner
  348. fn := func(resp api.ProgressResponse) error {
  349. if resp.Digest != "" {
  350. if spinner != nil {
  351. spinner.Stop()
  352. }
  353. bar, ok := bars[resp.Digest]
  354. if !ok {
  355. bar = progress.NewBar(fmt.Sprintf("pushing %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  356. bars[resp.Digest] = bar
  357. p.Add(resp.Digest, bar)
  358. }
  359. bar.Set(resp.Completed)
  360. } else if status != resp.Status {
  361. if spinner != nil {
  362. spinner.Stop()
  363. }
  364. status = resp.Status
  365. spinner = progress.NewSpinner(status)
  366. p.Add(status, spinner)
  367. }
  368. return nil
  369. }
  370. request := api.PushRequest{Name: args[0], Insecure: insecure}
  371. if err := client.Push(cmd.Context(), &request, fn); err != nil {
  372. if spinner != nil {
  373. spinner.Stop()
  374. }
  375. if strings.Contains(err.Error(), "access denied") {
  376. return errors.New("you are not authorized to push to this namespace, create the model under a namespace you own")
  377. }
  378. host := model.ParseName(args[0]).Host
  379. isOllamaHost := strings.HasSuffix(host, ".ollama.ai") || strings.HasSuffix(host, ".ollama.com")
  380. if strings.Contains(err.Error(), errtypes.UnknownOllamaKeyErrMsg) && isOllamaHost {
  381. // the user has not added their ollama key to ollama.com
  382. // re-throw an error with a more user-friendly message
  383. return errFromUnknownKey(err)
  384. }
  385. return err
  386. }
  387. spinner.Stop()
  388. return nil
  389. }
  390. func ListHandler(cmd *cobra.Command, args []string) error {
  391. client, err := api.ClientFromEnvironment()
  392. if err != nil {
  393. return err
  394. }
  395. models, err := client.List(cmd.Context())
  396. if err != nil {
  397. return err
  398. }
  399. var data [][]string
  400. for _, m := range models.Models {
  401. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  402. data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), format.HumanTime(m.ModifiedAt, "Never")})
  403. }
  404. }
  405. table := tablewriter.NewWriter(os.Stdout)
  406. table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
  407. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  408. table.SetAlignment(tablewriter.ALIGN_LEFT)
  409. table.SetHeaderLine(false)
  410. table.SetBorder(false)
  411. table.SetNoWhiteSpace(true)
  412. table.SetTablePadding("\t")
  413. table.AppendBulk(data)
  414. table.Render()
  415. return nil
  416. }
  417. func DeleteHandler(cmd *cobra.Command, args []string) error {
  418. client, err := api.ClientFromEnvironment()
  419. if err != nil {
  420. return err
  421. }
  422. for _, name := range args {
  423. req := api.DeleteRequest{Name: name}
  424. if err := client.Delete(cmd.Context(), &req); err != nil {
  425. return err
  426. }
  427. fmt.Printf("deleted '%s'\n", name)
  428. }
  429. return nil
  430. }
  431. func ShowHandler(cmd *cobra.Command, args []string) error {
  432. client, err := api.ClientFromEnvironment()
  433. if err != nil {
  434. return err
  435. }
  436. if len(args) != 1 {
  437. return errors.New("missing model name")
  438. }
  439. license, errLicense := cmd.Flags().GetBool("license")
  440. modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
  441. parameters, errParams := cmd.Flags().GetBool("parameters")
  442. system, errSystem := cmd.Flags().GetBool("system")
  443. template, errTemplate := cmd.Flags().GetBool("template")
  444. for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
  445. if boolErr != nil {
  446. return errors.New("error retrieving flags")
  447. }
  448. }
  449. flagsSet := 0
  450. showType := ""
  451. if license {
  452. flagsSet++
  453. showType = "license"
  454. }
  455. if modelfile {
  456. flagsSet++
  457. showType = "modelfile"
  458. }
  459. if parameters {
  460. flagsSet++
  461. showType = "parameters"
  462. }
  463. if system {
  464. flagsSet++
  465. showType = "system"
  466. }
  467. if template {
  468. flagsSet++
  469. showType = "template"
  470. }
  471. if flagsSet > 1 {
  472. return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
  473. } else if flagsSet == 0 {
  474. return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
  475. }
  476. req := api.ShowRequest{Name: args[0]}
  477. resp, err := client.Show(cmd.Context(), &req)
  478. if err != nil {
  479. return err
  480. }
  481. switch showType {
  482. case "license":
  483. fmt.Println(resp.License)
  484. case "modelfile":
  485. fmt.Println(resp.Modelfile)
  486. case "parameters":
  487. fmt.Println(resp.Parameters)
  488. case "system":
  489. fmt.Println(resp.System)
  490. case "template":
  491. fmt.Println(resp.Template)
  492. }
  493. return nil
  494. }
  495. func CopyHandler(cmd *cobra.Command, args []string) error {
  496. client, err := api.ClientFromEnvironment()
  497. if err != nil {
  498. return err
  499. }
  500. req := api.CopyRequest{Source: args[0], Destination: args[1]}
  501. if err := client.Copy(cmd.Context(), &req); err != nil {
  502. return err
  503. }
  504. fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
  505. return nil
  506. }
  507. func PullHandler(cmd *cobra.Command, args []string) error {
  508. insecure, err := cmd.Flags().GetBool("insecure")
  509. if err != nil {
  510. return err
  511. }
  512. client, err := api.ClientFromEnvironment()
  513. if err != nil {
  514. return err
  515. }
  516. p := progress.NewProgress(os.Stderr)
  517. defer p.Stop()
  518. bars := make(map[string]*progress.Bar)
  519. var status string
  520. var spinner *progress.Spinner
  521. fn := func(resp api.ProgressResponse) error {
  522. if resp.Digest != "" {
  523. if spinner != nil {
  524. spinner.Stop()
  525. }
  526. bar, ok := bars[resp.Digest]
  527. if !ok {
  528. bar = progress.NewBar(fmt.Sprintf("pulling %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  529. bars[resp.Digest] = bar
  530. p.Add(resp.Digest, bar)
  531. }
  532. bar.Set(resp.Completed)
  533. } else if status != resp.Status {
  534. if spinner != nil {
  535. spinner.Stop()
  536. }
  537. status = resp.Status
  538. spinner = progress.NewSpinner(status)
  539. p.Add(status, spinner)
  540. }
  541. return nil
  542. }
  543. request := api.PullRequest{Name: args[0], Insecure: insecure}
  544. if err := client.Pull(cmd.Context(), &request, fn); err != nil {
  545. return err
  546. }
  547. return nil
  548. }
  549. type generateContextKey string
  550. type runOptions struct {
  551. Model string
  552. ParentModel string
  553. Prompt string
  554. Messages []api.Message
  555. WordWrap bool
  556. Format string
  557. System string
  558. Template string
  559. Images []api.ImageData
  560. Options map[string]interface{}
  561. MultiModal bool
  562. }
  563. type displayResponseState struct {
  564. lineLength int
  565. wordBuffer string
  566. }
  567. func displayResponse(content string, wordWrap bool, state *displayResponseState) {
  568. termWidth, _, _ := term.GetSize(int(os.Stdout.Fd()))
  569. if wordWrap && termWidth >= 10 {
  570. for _, ch := range content {
  571. if state.lineLength+1 > termWidth-5 {
  572. if len(state.wordBuffer) > termWidth-10 {
  573. fmt.Printf("%s%c", state.wordBuffer, ch)
  574. state.wordBuffer = ""
  575. state.lineLength = 0
  576. continue
  577. }
  578. // backtrack the length of the last word and clear to the end of the line
  579. fmt.Printf("\x1b[%dD\x1b[K\n", len(state.wordBuffer))
  580. fmt.Printf("%s%c", state.wordBuffer, ch)
  581. state.lineLength = len(state.wordBuffer) + 1
  582. } else {
  583. fmt.Print(string(ch))
  584. state.lineLength += 1
  585. switch ch {
  586. case ' ':
  587. state.wordBuffer = ""
  588. case '\n':
  589. state.lineLength = 0
  590. default:
  591. state.wordBuffer += string(ch)
  592. }
  593. }
  594. }
  595. } else {
  596. fmt.Printf("%s%s", state.wordBuffer, content)
  597. if len(state.wordBuffer) > 0 {
  598. state.wordBuffer = ""
  599. }
  600. }
  601. }
  602. func chat(cmd *cobra.Command, opts runOptions) (*api.Message, error) {
  603. client, err := api.ClientFromEnvironment()
  604. if err != nil {
  605. return nil, err
  606. }
  607. p := progress.NewProgress(os.Stderr)
  608. defer p.StopAndClear()
  609. spinner := progress.NewSpinner("")
  610. p.Add("", spinner)
  611. cancelCtx, cancel := context.WithCancel(cmd.Context())
  612. defer cancel()
  613. sigChan := make(chan os.Signal, 1)
  614. signal.Notify(sigChan, syscall.SIGINT)
  615. go func() {
  616. <-sigChan
  617. cancel()
  618. }()
  619. var state *displayResponseState = &displayResponseState{}
  620. var latest api.ChatResponse
  621. var fullResponse strings.Builder
  622. var role string
  623. fn := func(response api.ChatResponse) error {
  624. p.StopAndClear()
  625. latest = response
  626. role = response.Message.Role
  627. content := response.Message.Content
  628. fullResponse.WriteString(content)
  629. displayResponse(content, opts.WordWrap, state)
  630. return nil
  631. }
  632. req := &api.ChatRequest{
  633. Model: opts.Model,
  634. Messages: opts.Messages,
  635. Format: opts.Format,
  636. Options: opts.Options,
  637. }
  638. if err := client.Chat(cancelCtx, req, fn); err != nil {
  639. if errors.Is(err, context.Canceled) {
  640. return nil, nil
  641. }
  642. return nil, err
  643. }
  644. if len(opts.Messages) > 0 {
  645. fmt.Println()
  646. fmt.Println()
  647. }
  648. verbose, err := cmd.Flags().GetBool("verbose")
  649. if err != nil {
  650. return nil, err
  651. }
  652. if verbose {
  653. latest.Summary()
  654. }
  655. return &api.Message{Role: role, Content: fullResponse.String()}, nil
  656. }
  657. func generate(cmd *cobra.Command, opts runOptions) error {
  658. client, err := api.ClientFromEnvironment()
  659. if err != nil {
  660. return err
  661. }
  662. p := progress.NewProgress(os.Stderr)
  663. defer p.StopAndClear()
  664. spinner := progress.NewSpinner("")
  665. p.Add("", spinner)
  666. var latest api.GenerateResponse
  667. generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
  668. if !ok {
  669. generateContext = []int{}
  670. }
  671. ctx, cancel := context.WithCancel(cmd.Context())
  672. defer cancel()
  673. sigChan := make(chan os.Signal, 1)
  674. signal.Notify(sigChan, syscall.SIGINT)
  675. go func() {
  676. <-sigChan
  677. cancel()
  678. }()
  679. var state *displayResponseState = &displayResponseState{}
  680. fn := func(response api.GenerateResponse) error {
  681. p.StopAndClear()
  682. latest = response
  683. content := response.Response
  684. displayResponse(content, opts.WordWrap, state)
  685. return nil
  686. }
  687. if opts.MultiModal {
  688. opts.Prompt, opts.Images, err = extractFileData(opts.Prompt)
  689. if err != nil {
  690. return err
  691. }
  692. }
  693. request := api.GenerateRequest{
  694. Model: opts.Model,
  695. Prompt: opts.Prompt,
  696. Context: generateContext,
  697. Images: opts.Images,
  698. Format: opts.Format,
  699. System: opts.System,
  700. Template: opts.Template,
  701. Options: opts.Options,
  702. }
  703. if err := client.Generate(ctx, &request, fn); err != nil {
  704. if errors.Is(err, context.Canceled) {
  705. return nil
  706. }
  707. return err
  708. }
  709. if opts.Prompt != "" {
  710. fmt.Println()
  711. fmt.Println()
  712. }
  713. if !latest.Done {
  714. return nil
  715. }
  716. verbose, err := cmd.Flags().GetBool("verbose")
  717. if err != nil {
  718. return err
  719. }
  720. if verbose {
  721. latest.Summary()
  722. }
  723. ctx = context.WithValue(cmd.Context(), generateContextKey("context"), latest.Context)
  724. cmd.SetContext(ctx)
  725. return nil
  726. }
  727. func RunServer(cmd *cobra.Command, _ []string) error {
  728. // retrieve the OLLAMA_HOST environment variable
  729. ollamaHost, err := api.GetOllamaHost()
  730. if err != nil {
  731. return err
  732. }
  733. if err := initializeKeypair(); err != nil {
  734. return err
  735. }
  736. ln, err := net.Listen("tcp", net.JoinHostPort(ollamaHost.Host, ollamaHost.Port))
  737. if err != nil {
  738. return err
  739. }
  740. return server.Serve(ln)
  741. }
  742. func initializeKeypair() error {
  743. home, err := os.UserHomeDir()
  744. if err != nil {
  745. return err
  746. }
  747. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  748. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  749. _, err = os.Stat(privKeyPath)
  750. if os.IsNotExist(err) {
  751. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  752. cryptoPublicKey, cryptoPrivateKey, err := ed25519.GenerateKey(rand.Reader)
  753. if err != nil {
  754. return err
  755. }
  756. privateKeyBytes, err := ssh.MarshalPrivateKey(cryptoPrivateKey, "")
  757. if err != nil {
  758. return err
  759. }
  760. if err := os.MkdirAll(filepath.Dir(privKeyPath), 0o755); err != nil {
  761. return fmt.Errorf("could not create directory %w", err)
  762. }
  763. if err := os.WriteFile(privKeyPath, pem.EncodeToMemory(privateKeyBytes), 0o600); err != nil {
  764. return err
  765. }
  766. sshPublicKey, err := ssh.NewPublicKey(cryptoPublicKey)
  767. if err != nil {
  768. return err
  769. }
  770. publicKeyBytes := ssh.MarshalAuthorizedKey(sshPublicKey)
  771. if err := os.WriteFile(pubKeyPath, publicKeyBytes, 0o644); err != nil {
  772. return err
  773. }
  774. fmt.Printf("Your new public key is: \n\n%s\n", publicKeyBytes)
  775. }
  776. return nil
  777. }
  778. //nolint:unused
  779. func waitForServer(ctx context.Context, client *api.Client) error {
  780. // wait for the server to start
  781. timeout := time.After(5 * time.Second)
  782. tick := time.Tick(500 * time.Millisecond)
  783. for {
  784. select {
  785. case <-timeout:
  786. return errors.New("timed out waiting for server to start")
  787. case <-tick:
  788. if err := client.Heartbeat(ctx); err == nil {
  789. return nil // server has started
  790. }
  791. }
  792. }
  793. }
  794. func checkServerHeartbeat(cmd *cobra.Command, _ []string) error {
  795. client, err := api.ClientFromEnvironment()
  796. if err != nil {
  797. return err
  798. }
  799. if err := client.Heartbeat(cmd.Context()); err != nil {
  800. if !strings.Contains(err.Error(), " refused") {
  801. return err
  802. }
  803. if err := startApp(cmd.Context(), client); err != nil {
  804. return fmt.Errorf("could not connect to ollama app, is it running?")
  805. }
  806. }
  807. return nil
  808. }
  809. func versionHandler(cmd *cobra.Command, _ []string) {
  810. client, err := api.ClientFromEnvironment()
  811. if err != nil {
  812. return
  813. }
  814. serverVersion, err := client.Version(cmd.Context())
  815. if err != nil {
  816. fmt.Println("Warning: could not connect to a running Ollama instance")
  817. }
  818. if serverVersion != "" {
  819. fmt.Printf("ollama version is %s\n", serverVersion)
  820. }
  821. if serverVersion != version.Version {
  822. fmt.Printf("Warning: client version is %s\n", version.Version)
  823. }
  824. }
  825. func appendHostEnvDocs(cmd *cobra.Command) {
  826. const hostEnvDocs = `
  827. Environment Variables:
  828. OLLAMA_HOST The host:port or base URL of the Ollama server (e.g. http://localhost:11434)
  829. `
  830. cmd.SetUsageTemplate(cmd.UsageTemplate() + hostEnvDocs)
  831. }
  832. func NewCLI() *cobra.Command {
  833. log.SetFlags(log.LstdFlags | log.Lshortfile)
  834. cobra.EnableCommandSorting = false
  835. if runtime.GOOS == "windows" {
  836. console.ConsoleFromFile(os.Stdin) //nolint:errcheck
  837. }
  838. rootCmd := &cobra.Command{
  839. Use: "ollama",
  840. Short: "Large language model runner",
  841. SilenceUsage: true,
  842. SilenceErrors: true,
  843. CompletionOptions: cobra.CompletionOptions{
  844. DisableDefaultCmd: true,
  845. },
  846. Run: func(cmd *cobra.Command, args []string) {
  847. if version, _ := cmd.Flags().GetBool("version"); version {
  848. versionHandler(cmd, args)
  849. return
  850. }
  851. cmd.Print(cmd.UsageString())
  852. },
  853. }
  854. rootCmd.Flags().BoolP("version", "v", false, "Show version information")
  855. createCmd := &cobra.Command{
  856. Use: "create MODEL",
  857. Short: "Create a model from a Modelfile",
  858. Args: cobra.ExactArgs(1),
  859. PreRunE: checkServerHeartbeat,
  860. RunE: CreateHandler,
  861. }
  862. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  863. createCmd.Flags().StringP("quantization", "q", "", "Quantization level.")
  864. showCmd := &cobra.Command{
  865. Use: "show MODEL",
  866. Short: "Show information for a model",
  867. Args: cobra.ExactArgs(1),
  868. PreRunE: checkServerHeartbeat,
  869. RunE: ShowHandler,
  870. }
  871. showCmd.Flags().Bool("license", false, "Show license of a model")
  872. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  873. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  874. showCmd.Flags().Bool("template", false, "Show template of a model")
  875. showCmd.Flags().Bool("system", false, "Show system message of a model")
  876. runCmd := &cobra.Command{
  877. Use: "run MODEL [PROMPT]",
  878. Short: "Run a model",
  879. Args: cobra.MinimumNArgs(1),
  880. PreRunE: checkServerHeartbeat,
  881. RunE: RunHandler,
  882. }
  883. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  884. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  885. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  886. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  887. serveCmd := &cobra.Command{
  888. Use: "serve",
  889. Aliases: []string{"start"},
  890. Short: "Start ollama",
  891. Args: cobra.ExactArgs(0),
  892. RunE: RunServer,
  893. }
  894. serveCmd.SetUsageTemplate(serveCmd.UsageTemplate() + `
  895. Environment Variables:
  896. OLLAMA_HOST The host:port to bind to (default "127.0.0.1:11434")
  897. OLLAMA_ORIGINS A comma separated list of allowed origins.
  898. OLLAMA_MODELS The path to the models directory (default is "~/.ollama/models")
  899. OLLAMA_KEEP_ALIVE The duration that models stay loaded in memory (default is "5m")
  900. OLLAMA_DEBUG Set to 1 to enable additional debug logging
  901. `)
  902. pullCmd := &cobra.Command{
  903. Use: "pull MODEL",
  904. Short: "Pull a model from a registry",
  905. Args: cobra.ExactArgs(1),
  906. PreRunE: checkServerHeartbeat,
  907. RunE: PullHandler,
  908. }
  909. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  910. pushCmd := &cobra.Command{
  911. Use: "push MODEL",
  912. Short: "Push a model to a registry",
  913. Args: cobra.ExactArgs(1),
  914. PreRunE: checkServerHeartbeat,
  915. RunE: PushHandler,
  916. }
  917. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  918. listCmd := &cobra.Command{
  919. Use: "list",
  920. Aliases: []string{"ls"},
  921. Short: "List models",
  922. PreRunE: checkServerHeartbeat,
  923. RunE: ListHandler,
  924. }
  925. copyCmd := &cobra.Command{
  926. Use: "cp SOURCE TARGET",
  927. Short: "Copy a model",
  928. Args: cobra.ExactArgs(2),
  929. PreRunE: checkServerHeartbeat,
  930. RunE: CopyHandler,
  931. }
  932. deleteCmd := &cobra.Command{
  933. Use: "rm MODEL [MODEL...]",
  934. Short: "Remove a model",
  935. Args: cobra.MinimumNArgs(1),
  936. PreRunE: checkServerHeartbeat,
  937. RunE: DeleteHandler,
  938. }
  939. for _, cmd := range []*cobra.Command{
  940. createCmd,
  941. showCmd,
  942. runCmd,
  943. pullCmd,
  944. pushCmd,
  945. listCmd,
  946. copyCmd,
  947. deleteCmd,
  948. } {
  949. appendHostEnvDocs(cmd)
  950. }
  951. rootCmd.AddCommand(
  952. serveCmd,
  953. createCmd,
  954. showCmd,
  955. runCmd,
  956. pullCmd,
  957. pushCmd,
  958. listCmd,
  959. copyCmd,
  960. deleteCmd,
  961. )
  962. return rootCmd
  963. }