cmd.go 21 KB

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