cmd.go 23 KB

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