cmd.go 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  1. package cmd
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/ed25519"
  6. "crypto/rand"
  7. "crypto/sha256"
  8. "encoding/pem"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "log"
  13. "net"
  14. "net/http"
  15. "os"
  16. "os/exec"
  17. "os/signal"
  18. "path/filepath"
  19. "runtime"
  20. "strings"
  21. "syscall"
  22. "time"
  23. "github.com/olekukonko/tablewriter"
  24. "github.com/spf13/cobra"
  25. "golang.org/x/crypto/ssh"
  26. "golang.org/x/term"
  27. "github.com/jmorganca/ollama/api"
  28. "github.com/jmorganca/ollama/format"
  29. "github.com/jmorganca/ollama/parser"
  30. "github.com/jmorganca/ollama/progress"
  31. "github.com/jmorganca/ollama/readline"
  32. "github.com/jmorganca/ollama/server"
  33. "github.com/jmorganca/ollama/version"
  34. )
  35. func CreateHandler(cmd *cobra.Command, args []string) error {
  36. filename, _ := cmd.Flags().GetString("file")
  37. filename, err := filepath.Abs(filename)
  38. if err != nil {
  39. return err
  40. }
  41. client, err := api.ClientFromEnvironment()
  42. if err != nil {
  43. return err
  44. }
  45. p := progress.NewProgress(os.Stderr)
  46. defer p.Stop()
  47. bars := make(map[string]*progress.Bar)
  48. modelfile, err := os.ReadFile(filename)
  49. if err != nil {
  50. return err
  51. }
  52. commands, err := parser.Parse(bytes.NewReader(modelfile))
  53. if err != nil {
  54. return err
  55. }
  56. home, err := os.UserHomeDir()
  57. if err != nil {
  58. return err
  59. }
  60. status := "transferring model data"
  61. spinner := progress.NewSpinner(status)
  62. p.Add(status, spinner)
  63. for _, c := range commands {
  64. switch c.Name {
  65. case "model", "adapter":
  66. path := c.Args
  67. if path == "~" {
  68. path = home
  69. } else if strings.HasPrefix(path, "~/") {
  70. path = filepath.Join(home, path[2:])
  71. }
  72. bin, err := os.Open(path)
  73. if errors.Is(err, os.ErrNotExist) && c.Name == "model" {
  74. continue
  75. } else if err != nil {
  76. return err
  77. }
  78. defer bin.Close()
  79. hash := sha256.New()
  80. if _, err := io.Copy(hash, bin); err != nil {
  81. return err
  82. }
  83. bin.Seek(0, io.SeekStart)
  84. digest := fmt.Sprintf("sha256:%x", hash.Sum(nil))
  85. if err = client.CreateBlob(cmd.Context(), digest, bin); err != nil {
  86. return err
  87. }
  88. modelfile = bytes.ReplaceAll(modelfile, []byte(c.Args), []byte("@"+digest))
  89. }
  90. }
  91. fn := func(resp api.ProgressResponse) error {
  92. if resp.Digest != "" {
  93. spinner.Stop()
  94. bar, ok := bars[resp.Digest]
  95. if !ok {
  96. bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
  97. bars[resp.Digest] = bar
  98. p.Add(resp.Digest, bar)
  99. }
  100. bar.Set(resp.Completed)
  101. } else if status != resp.Status {
  102. spinner.Stop()
  103. status = resp.Status
  104. spinner = progress.NewSpinner(status)
  105. p.Add(status, spinner)
  106. }
  107. return nil
  108. }
  109. request := api.CreateRequest{Name: args[0], Path: filename, Modelfile: string(modelfile)}
  110. if err := client.Create(context.Background(), &request, fn); err != nil {
  111. return err
  112. }
  113. return nil
  114. }
  115. func RunHandler(cmd *cobra.Command, args []string) error {
  116. client, err := api.ClientFromEnvironment()
  117. if err != nil {
  118. return err
  119. }
  120. name := args[0]
  121. // check if the model exists on the server
  122. _, err = client.Show(context.Background(), &api.ShowRequest{Name: name})
  123. var statusError api.StatusError
  124. switch {
  125. case errors.As(err, &statusError) && statusError.StatusCode == http.StatusNotFound:
  126. if err := PullHandler(cmd, args); err != nil {
  127. return err
  128. }
  129. case err != nil:
  130. return err
  131. }
  132. return RunGenerate(cmd, args)
  133. }
  134. func PushHandler(cmd *cobra.Command, args []string) error {
  135. client, err := api.ClientFromEnvironment()
  136. if err != nil {
  137. return err
  138. }
  139. insecure, err := cmd.Flags().GetBool("insecure")
  140. if err != nil {
  141. return err
  142. }
  143. p := progress.NewProgress(os.Stderr)
  144. defer p.Stop()
  145. bars := make(map[string]*progress.Bar)
  146. var status string
  147. var spinner *progress.Spinner
  148. fn := func(resp api.ProgressResponse) error {
  149. if resp.Digest != "" {
  150. if spinner != nil {
  151. spinner.Stop()
  152. }
  153. bar, ok := bars[resp.Digest]
  154. if !ok {
  155. bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
  156. bars[resp.Digest] = bar
  157. p.Add(resp.Digest, bar)
  158. }
  159. bar.Set(resp.Completed)
  160. } else if status != resp.Status {
  161. if spinner != nil {
  162. spinner.Stop()
  163. }
  164. status = resp.Status
  165. spinner = progress.NewSpinner(status)
  166. p.Add(status, spinner)
  167. }
  168. return nil
  169. }
  170. request := api.PushRequest{Name: args[0], Insecure: insecure}
  171. if err := client.Push(context.Background(), &request, fn); err != nil {
  172. return err
  173. }
  174. spinner.Stop()
  175. return nil
  176. }
  177. func ListHandler(cmd *cobra.Command, args []string) error {
  178. client, err := api.ClientFromEnvironment()
  179. if err != nil {
  180. return err
  181. }
  182. models, err := client.List(context.Background())
  183. if err != nil {
  184. return err
  185. }
  186. var data [][]string
  187. for _, m := range models.Models {
  188. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  189. data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), format.HumanTime(m.ModifiedAt, "Never")})
  190. }
  191. }
  192. table := tablewriter.NewWriter(os.Stdout)
  193. table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
  194. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  195. table.SetAlignment(tablewriter.ALIGN_LEFT)
  196. table.SetHeaderLine(false)
  197. table.SetBorder(false)
  198. table.SetNoWhiteSpace(true)
  199. table.SetTablePadding("\t")
  200. table.AppendBulk(data)
  201. table.Render()
  202. return nil
  203. }
  204. func DeleteHandler(cmd *cobra.Command, args []string) error {
  205. client, err := api.ClientFromEnvironment()
  206. if err != nil {
  207. return err
  208. }
  209. for _, name := range args {
  210. req := api.DeleteRequest{Name: name}
  211. if err := client.Delete(context.Background(), &req); err != nil {
  212. return err
  213. }
  214. fmt.Printf("deleted '%s'\n", name)
  215. }
  216. return nil
  217. }
  218. func ShowHandler(cmd *cobra.Command, args []string) error {
  219. client, err := api.ClientFromEnvironment()
  220. if err != nil {
  221. return err
  222. }
  223. if len(args) != 1 {
  224. return errors.New("missing model name")
  225. }
  226. license, errLicense := cmd.Flags().GetBool("license")
  227. modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
  228. parameters, errParams := cmd.Flags().GetBool("parameters")
  229. system, errSystem := cmd.Flags().GetBool("system")
  230. template, errTemplate := cmd.Flags().GetBool("template")
  231. for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
  232. if boolErr != nil {
  233. return errors.New("error retrieving flags")
  234. }
  235. }
  236. flagsSet := 0
  237. showType := ""
  238. if license {
  239. flagsSet++
  240. showType = "license"
  241. }
  242. if modelfile {
  243. flagsSet++
  244. showType = "modelfile"
  245. }
  246. if parameters {
  247. flagsSet++
  248. showType = "parameters"
  249. }
  250. if system {
  251. flagsSet++
  252. showType = "system"
  253. }
  254. if template {
  255. flagsSet++
  256. showType = "template"
  257. }
  258. if flagsSet > 1 {
  259. return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
  260. } else if flagsSet == 0 {
  261. return errors.New("one of '--license', '--modelfile', '--parameters', '--system', or '--template' must be specified")
  262. }
  263. req := api.ShowRequest{Name: args[0]}
  264. resp, err := client.Show(context.Background(), &req)
  265. if err != nil {
  266. return err
  267. }
  268. switch showType {
  269. case "license":
  270. fmt.Println(resp.License)
  271. case "modelfile":
  272. fmt.Println(resp.Modelfile)
  273. case "parameters":
  274. fmt.Println(resp.Parameters)
  275. case "system":
  276. fmt.Println(resp.System)
  277. case "template":
  278. fmt.Println(resp.Template)
  279. }
  280. return nil
  281. }
  282. func CopyHandler(cmd *cobra.Command, args []string) error {
  283. client, err := api.ClientFromEnvironment()
  284. if err != nil {
  285. return err
  286. }
  287. req := api.CopyRequest{Source: args[0], Destination: args[1]}
  288. if err := client.Copy(context.Background(), &req); err != nil {
  289. return err
  290. }
  291. fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
  292. return nil
  293. }
  294. func PullHandler(cmd *cobra.Command, args []string) error {
  295. insecure, err := cmd.Flags().GetBool("insecure")
  296. if err != nil {
  297. return err
  298. }
  299. client, err := api.ClientFromEnvironment()
  300. if err != nil {
  301. return err
  302. }
  303. p := progress.NewProgress(os.Stderr)
  304. defer p.Stop()
  305. bars := make(map[string]*progress.Bar)
  306. var status string
  307. var spinner *progress.Spinner
  308. fn := func(resp api.ProgressResponse) error {
  309. if resp.Digest != "" {
  310. if spinner != nil {
  311. spinner.Stop()
  312. }
  313. bar, ok := bars[resp.Digest]
  314. if !ok {
  315. bar = progress.NewBar(resp.Status, resp.Total, resp.Completed)
  316. bars[resp.Digest] = bar
  317. p.Add(resp.Digest, bar)
  318. }
  319. bar.Set(resp.Completed)
  320. } else if status != resp.Status {
  321. if spinner != nil {
  322. spinner.Stop()
  323. }
  324. status = resp.Status
  325. spinner = progress.NewSpinner(status)
  326. p.Add(status, spinner)
  327. }
  328. return nil
  329. }
  330. request := api.PullRequest{Name: args[0], Insecure: insecure}
  331. if err := client.Pull(context.Background(), &request, fn); err != nil {
  332. return err
  333. }
  334. return nil
  335. }
  336. func RunGenerate(cmd *cobra.Command, args []string) error {
  337. format, err := cmd.Flags().GetString("format")
  338. if err != nil {
  339. return err
  340. }
  341. prompts := args[1:]
  342. // prepend stdin to the prompt if provided
  343. if !term.IsTerminal(int(os.Stdin.Fd())) {
  344. in, err := io.ReadAll(os.Stdin)
  345. if err != nil {
  346. return err
  347. }
  348. prompts = append([]string{string(in)}, prompts...)
  349. }
  350. // output is being piped
  351. if !term.IsTerminal(int(os.Stdout.Fd())) {
  352. return generate(cmd, args[0], strings.Join(prompts, " "), false, format)
  353. }
  354. wordWrap := os.Getenv("TERM") == "xterm-256color"
  355. nowrap, err := cmd.Flags().GetBool("nowordwrap")
  356. if err != nil {
  357. return err
  358. }
  359. if nowrap {
  360. wordWrap = false
  361. }
  362. // prompts are provided via stdin or args so don't enter interactive mode
  363. if len(prompts) > 0 {
  364. return generate(cmd, args[0], strings.Join(prompts, " "), wordWrap, format)
  365. }
  366. return generateInteractive(cmd, args[0], wordWrap, format)
  367. }
  368. type generateContextKey string
  369. func generate(cmd *cobra.Command, model, prompt string, wordWrap bool, format string) error {
  370. client, err := api.ClientFromEnvironment()
  371. if err != nil {
  372. return err
  373. }
  374. p := progress.NewProgress(os.Stderr)
  375. defer p.StopAndClear()
  376. spinner := progress.NewSpinner("")
  377. p.Add("", spinner)
  378. var latest api.GenerateResponse
  379. generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
  380. if !ok {
  381. generateContext = []int{}
  382. }
  383. termWidth, _, err := term.GetSize(int(os.Stdout.Fd()))
  384. if err != nil {
  385. wordWrap = false
  386. }
  387. cancelCtx, cancel := context.WithCancel(context.Background())
  388. defer cancel()
  389. sigChan := make(chan os.Signal, 1)
  390. signal.Notify(sigChan, syscall.SIGINT)
  391. var abort bool
  392. go func() {
  393. <-sigChan
  394. cancel()
  395. abort = true
  396. }()
  397. var currentLineLength int
  398. var wordBuffer string
  399. request := api.GenerateRequest{Model: model, Prompt: prompt, Context: generateContext, Format: format}
  400. fn := func(response api.GenerateResponse) error {
  401. p.StopAndClear()
  402. latest = response
  403. if wordWrap {
  404. for _, ch := range response.Response {
  405. if currentLineLength+1 > termWidth-5 {
  406. // backtrack the length of the last word and clear to the end of the line
  407. fmt.Printf("\x1b[%dD\x1b[K\n", len(wordBuffer))
  408. fmt.Printf("%s%c", wordBuffer, ch)
  409. currentLineLength = len(wordBuffer) + 1
  410. } else {
  411. fmt.Print(string(ch))
  412. currentLineLength += 1
  413. switch ch {
  414. case ' ':
  415. wordBuffer = ""
  416. case '\n':
  417. currentLineLength = 0
  418. default:
  419. wordBuffer += string(ch)
  420. }
  421. }
  422. }
  423. } else {
  424. fmt.Print(response.Response)
  425. }
  426. return nil
  427. }
  428. if err := client.Generate(cancelCtx, &request, fn); err != nil {
  429. if strings.Contains(err.Error(), "context canceled") && abort {
  430. return nil
  431. }
  432. return err
  433. }
  434. if prompt != "" {
  435. fmt.Println()
  436. fmt.Println()
  437. }
  438. if !latest.Done {
  439. if abort {
  440. return nil
  441. }
  442. return errors.New("unexpected end of response")
  443. }
  444. verbose, err := cmd.Flags().GetBool("verbose")
  445. if err != nil {
  446. return err
  447. }
  448. if verbose {
  449. latest.Summary()
  450. }
  451. ctx := cmd.Context()
  452. ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
  453. cmd.SetContext(ctx)
  454. return nil
  455. }
  456. func generateInteractive(cmd *cobra.Command, model string, wordWrap bool, format string) error {
  457. // load the model
  458. if err := generate(cmd, model, "", false, ""); err != nil {
  459. return err
  460. }
  461. usage := func() {
  462. fmt.Fprintln(os.Stderr, "Available Commands:")
  463. fmt.Fprintln(os.Stderr, " /set Set session variables")
  464. fmt.Fprintln(os.Stderr, " /show Show model information")
  465. fmt.Fprintln(os.Stderr, " /bye Exit")
  466. fmt.Fprintln(os.Stderr, " /?, /help Help for a command")
  467. fmt.Fprintln(os.Stderr, "")
  468. fmt.Fprintln(os.Stderr, "Use \"\"\" to begin a multi-line message.")
  469. fmt.Fprintln(os.Stderr, "")
  470. }
  471. usageSet := func() {
  472. fmt.Fprintln(os.Stderr, "Available Commands:")
  473. fmt.Fprintln(os.Stderr, " /set history Enable history")
  474. fmt.Fprintln(os.Stderr, " /set nohistory Disable history")
  475. fmt.Fprintln(os.Stderr, " /set wordwrap Enable wordwrap")
  476. fmt.Fprintln(os.Stderr, " /set nowordwrap Disable wordwrap")
  477. fmt.Fprintln(os.Stderr, " /set format json Enable JSON mode")
  478. fmt.Fprintln(os.Stderr, " /set noformat Disable formatting")
  479. fmt.Fprintln(os.Stderr, " /set verbose Show LLM stats")
  480. fmt.Fprintln(os.Stderr, " /set quiet Disable LLM stats")
  481. fmt.Fprintln(os.Stderr, "")
  482. }
  483. usageShow := func() {
  484. fmt.Fprintln(os.Stderr, "Available Commands:")
  485. fmt.Fprintln(os.Stderr, " /show license Show model license")
  486. fmt.Fprintln(os.Stderr, " /show modelfile Show Modelfile for this model")
  487. fmt.Fprintln(os.Stderr, " /show parameters Show parameters for this model")
  488. fmt.Fprintln(os.Stderr, " /show system Show system prompt")
  489. fmt.Fprintln(os.Stderr, " /show template Show prompt template")
  490. fmt.Fprintln(os.Stderr, "")
  491. }
  492. prompt := readline.Prompt{
  493. Prompt: ">>> ",
  494. AltPrompt: "... ",
  495. Placeholder: "Send a message (/? for help)",
  496. AltPlaceholder: `Use """ to end multi-line input`,
  497. }
  498. scanner, err := readline.New(prompt)
  499. if err != nil {
  500. return err
  501. }
  502. fmt.Print(readline.StartBracketedPaste)
  503. defer fmt.Printf(readline.EndBracketedPaste)
  504. var multiLineBuffer string
  505. for {
  506. line, err := scanner.Readline()
  507. switch {
  508. case errors.Is(err, io.EOF):
  509. fmt.Println()
  510. return nil
  511. case errors.Is(err, readline.ErrInterrupt):
  512. if line == "" {
  513. fmt.Println("\nUse Ctrl-D or /bye to exit.")
  514. }
  515. continue
  516. case err != nil:
  517. return err
  518. }
  519. line = strings.TrimSpace(line)
  520. switch {
  521. case scanner.Prompt.UseAlt:
  522. if strings.HasSuffix(line, `"""`) {
  523. scanner.Prompt.UseAlt = false
  524. multiLineBuffer += strings.TrimSuffix(line, `"""`)
  525. line = multiLineBuffer
  526. multiLineBuffer = ""
  527. } else {
  528. multiLineBuffer += line + " "
  529. continue
  530. }
  531. case strings.HasPrefix(line, `"""`):
  532. scanner.Prompt.UseAlt = true
  533. multiLineBuffer = strings.TrimPrefix(line, `"""`) + " "
  534. continue
  535. case strings.HasPrefix(line, "/list"):
  536. args := strings.Fields(line)
  537. if err := ListHandler(cmd, args[1:]); err != nil {
  538. return err
  539. }
  540. case strings.HasPrefix(line, "/set"):
  541. args := strings.Fields(line)
  542. if len(args) > 1 {
  543. switch args[1] {
  544. case "history":
  545. scanner.HistoryEnable()
  546. case "nohistory":
  547. scanner.HistoryDisable()
  548. case "wordwrap":
  549. wordWrap = true
  550. fmt.Println("Set 'wordwrap' mode.")
  551. case "nowordwrap":
  552. wordWrap = false
  553. fmt.Println("Set 'nowordwrap' mode.")
  554. case "verbose":
  555. cmd.Flags().Set("verbose", "true")
  556. fmt.Println("Set 'verbose' mode.")
  557. case "quiet":
  558. cmd.Flags().Set("verbose", "false")
  559. fmt.Println("Set 'quiet' mode.")
  560. case "format":
  561. if len(args) < 3 || args[2] != "json" {
  562. fmt.Println("Invalid or missing format. For 'json' mode use '/set format json'")
  563. } else {
  564. format = args[2]
  565. fmt.Printf("Set format to '%s' mode.\n", args[2])
  566. }
  567. case "noformat":
  568. format = ""
  569. fmt.Println("Disabled format.")
  570. default:
  571. fmt.Printf("Unknown command '/set %s'. Type /? for help\n", args[1])
  572. }
  573. } else {
  574. usageSet()
  575. }
  576. case strings.HasPrefix(line, "/show"):
  577. args := strings.Fields(line)
  578. if len(args) > 1 {
  579. client, err := api.ClientFromEnvironment()
  580. if err != nil {
  581. fmt.Println("error: couldn't connect to ollama server")
  582. return err
  583. }
  584. resp, err := client.Show(cmd.Context(), &api.ShowRequest{Name: model})
  585. if err != nil {
  586. fmt.Println("error: couldn't get model")
  587. return err
  588. }
  589. switch args[1] {
  590. case "license":
  591. if resp.License == "" {
  592. fmt.Print("No license was specified for this model.\n\n")
  593. } else {
  594. fmt.Println(resp.License)
  595. }
  596. case "modelfile":
  597. fmt.Println(resp.Modelfile)
  598. case "parameters":
  599. if resp.Parameters == "" {
  600. fmt.Print("No parameters were specified for this model.\n\n")
  601. } else {
  602. fmt.Println(resp.Parameters)
  603. }
  604. case "system":
  605. if resp.System == "" {
  606. fmt.Print("No system prompt was specified for this model.\n\n")
  607. } else {
  608. fmt.Println(resp.System)
  609. }
  610. case "template":
  611. if resp.Template == "" {
  612. fmt.Print("No prompt template was specified for this model.\n\n")
  613. } else {
  614. fmt.Println(resp.Template)
  615. }
  616. default:
  617. fmt.Printf("Unknown command '/show %s'. Type /? for help\n", args[1])
  618. }
  619. } else {
  620. usageShow()
  621. }
  622. case strings.HasPrefix(line, "/help"), strings.HasPrefix(line, "/?"):
  623. args := strings.Fields(line)
  624. if len(args) > 1 {
  625. switch args[1] {
  626. case "set", "/set":
  627. usageSet()
  628. case "show", "/show":
  629. usageShow()
  630. }
  631. } else {
  632. usage()
  633. }
  634. case line == "/exit", line == "/bye":
  635. return nil
  636. case strings.HasPrefix(line, "/"):
  637. args := strings.Fields(line)
  638. fmt.Printf("Unknown command '%s'. Type /? for help\n", args[0])
  639. }
  640. if len(line) > 0 && line[0] != '/' {
  641. if err := generate(cmd, model, line, wordWrap, format); err != nil {
  642. return err
  643. }
  644. }
  645. }
  646. }
  647. func RunServer(cmd *cobra.Command, _ []string) error {
  648. host, port, err := net.SplitHostPort(os.Getenv("OLLAMA_HOST"))
  649. if err != nil {
  650. host, port = "127.0.0.1", "11434"
  651. if ip := net.ParseIP(strings.Trim(os.Getenv("OLLAMA_HOST"), "[]")); ip != nil {
  652. host = ip.String()
  653. }
  654. }
  655. if err := initializeKeypair(); err != nil {
  656. return err
  657. }
  658. ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
  659. if err != nil {
  660. return err
  661. }
  662. var origins []string
  663. if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
  664. origins = strings.Split(o, ",")
  665. }
  666. return server.Serve(ln, origins)
  667. }
  668. func initializeKeypair() error {
  669. home, err := os.UserHomeDir()
  670. if err != nil {
  671. return err
  672. }
  673. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  674. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  675. _, err = os.Stat(privKeyPath)
  676. if os.IsNotExist(err) {
  677. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  678. _, privKey, err := ed25519.GenerateKey(rand.Reader)
  679. if err != nil {
  680. return err
  681. }
  682. privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
  683. if err != nil {
  684. return err
  685. }
  686. err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
  687. if err != nil {
  688. return fmt.Errorf("could not create directory %w", err)
  689. }
  690. err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
  691. if err != nil {
  692. return err
  693. }
  694. sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
  695. if err != nil {
  696. return err
  697. }
  698. pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())
  699. err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
  700. if err != nil {
  701. return err
  702. }
  703. fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
  704. }
  705. return nil
  706. }
  707. func startMacApp(client *api.Client) error {
  708. exe, err := os.Executable()
  709. if err != nil {
  710. return err
  711. }
  712. link, err := os.Readlink(exe)
  713. if err != nil {
  714. return err
  715. }
  716. if !strings.Contains(link, "Ollama.app") {
  717. return fmt.Errorf("could not find ollama app")
  718. }
  719. path := strings.Split(link, "Ollama.app")
  720. if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
  721. return err
  722. }
  723. // wait for the server to start
  724. timeout := time.After(5 * time.Second)
  725. tick := time.Tick(500 * time.Millisecond)
  726. for {
  727. select {
  728. case <-timeout:
  729. return errors.New("timed out waiting for server to start")
  730. case <-tick:
  731. if err := client.Heartbeat(context.Background()); err == nil {
  732. return nil // server has started
  733. }
  734. }
  735. }
  736. }
  737. func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
  738. client, err := api.ClientFromEnvironment()
  739. if err != nil {
  740. return err
  741. }
  742. if err := client.Heartbeat(context.Background()); err != nil {
  743. if !strings.Contains(err.Error(), "connection refused") {
  744. return err
  745. }
  746. if runtime.GOOS == "darwin" {
  747. if err := startMacApp(client); err != nil {
  748. return fmt.Errorf("could not connect to ollama app, is it running?")
  749. }
  750. } else {
  751. return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
  752. }
  753. }
  754. return nil
  755. }
  756. func NewCLI() *cobra.Command {
  757. log.SetFlags(log.LstdFlags | log.Lshortfile)
  758. rootCmd := &cobra.Command{
  759. Use: "ollama",
  760. Short: "Large language model runner",
  761. SilenceUsage: true,
  762. SilenceErrors: true,
  763. CompletionOptions: cobra.CompletionOptions{
  764. DisableDefaultCmd: true,
  765. },
  766. Version: version.Version,
  767. }
  768. cobra.EnableCommandSorting = false
  769. createCmd := &cobra.Command{
  770. Use: "create MODEL",
  771. Short: "Create a model from a Modelfile",
  772. Args: cobra.ExactArgs(1),
  773. PreRunE: checkServerHeartbeat,
  774. RunE: CreateHandler,
  775. }
  776. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  777. showCmd := &cobra.Command{
  778. Use: "show MODEL",
  779. Short: "Show information for a model",
  780. Args: cobra.ExactArgs(1),
  781. PreRunE: checkServerHeartbeat,
  782. RunE: ShowHandler,
  783. }
  784. showCmd.Flags().Bool("license", false, "Show license of a model")
  785. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  786. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  787. showCmd.Flags().Bool("template", false, "Show template of a model")
  788. showCmd.Flags().Bool("system", false, "Show system prompt of a model")
  789. runCmd := &cobra.Command{
  790. Use: "run MODEL [PROMPT]",
  791. Short: "Run a model",
  792. Args: cobra.MinimumNArgs(1),
  793. PreRunE: checkServerHeartbeat,
  794. RunE: RunHandler,
  795. }
  796. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  797. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  798. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  799. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  800. serveCmd := &cobra.Command{
  801. Use: "serve",
  802. Aliases: []string{"start"},
  803. Short: "Start ollama",
  804. Args: cobra.ExactArgs(0),
  805. RunE: RunServer,
  806. }
  807. pullCmd := &cobra.Command{
  808. Use: "pull MODEL",
  809. Short: "Pull a model from a registry",
  810. Args: cobra.ExactArgs(1),
  811. PreRunE: checkServerHeartbeat,
  812. RunE: PullHandler,
  813. }
  814. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  815. pushCmd := &cobra.Command{
  816. Use: "push MODEL",
  817. Short: "Push a model to a registry",
  818. Args: cobra.ExactArgs(1),
  819. PreRunE: checkServerHeartbeat,
  820. RunE: PushHandler,
  821. }
  822. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  823. listCmd := &cobra.Command{
  824. Use: "list",
  825. Aliases: []string{"ls"},
  826. Short: "List models",
  827. PreRunE: checkServerHeartbeat,
  828. RunE: ListHandler,
  829. }
  830. copyCmd := &cobra.Command{
  831. Use: "cp SOURCE TARGET",
  832. Short: "Copy a model",
  833. Args: cobra.ExactArgs(2),
  834. PreRunE: checkServerHeartbeat,
  835. RunE: CopyHandler,
  836. }
  837. deleteCmd := &cobra.Command{
  838. Use: "rm MODEL [MODEL...]",
  839. Short: "Remove a model",
  840. Args: cobra.MinimumNArgs(1),
  841. PreRunE: checkServerHeartbeat,
  842. RunE: DeleteHandler,
  843. }
  844. rootCmd.AddCommand(
  845. serveCmd,
  846. createCmd,
  847. showCmd,
  848. runCmd,
  849. pullCmd,
  850. pushCmd,
  851. listCmd,
  852. copyCmd,
  853. deleteCmd,
  854. )
  855. return rootCmd
  856. }