cmd.go 23 KB

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