cmd.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  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.Stop()
  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. spinner.Stop()
  402. p.StopAndClear()
  403. latest = response
  404. if wordWrap {
  405. for _, ch := range response.Response {
  406. if currentLineLength+1 > termWidth-5 {
  407. // backtrack the length of the last word and clear to the end of the line
  408. fmt.Printf("\x1b[%dD\x1b[K\n", len(wordBuffer))
  409. fmt.Printf("%s%c", wordBuffer, ch)
  410. currentLineLength = len(wordBuffer) + 1
  411. } else {
  412. fmt.Print(string(ch))
  413. currentLineLength += 1
  414. switch ch {
  415. case ' ':
  416. wordBuffer = ""
  417. case '\n':
  418. currentLineLength = 0
  419. default:
  420. wordBuffer += string(ch)
  421. }
  422. }
  423. }
  424. } else {
  425. fmt.Print(response.Response)
  426. }
  427. return nil
  428. }
  429. if err := client.Generate(cancelCtx, &request, fn); err != nil {
  430. if strings.Contains(err.Error(), "context canceled") && abort {
  431. return nil
  432. }
  433. return err
  434. }
  435. if prompt != "" {
  436. fmt.Println()
  437. fmt.Println()
  438. }
  439. if !latest.Done {
  440. if abort {
  441. return nil
  442. }
  443. return errors.New("unexpected end of response")
  444. }
  445. verbose, err := cmd.Flags().GetBool("verbose")
  446. if err != nil {
  447. return err
  448. }
  449. if verbose {
  450. latest.Summary()
  451. }
  452. ctx := cmd.Context()
  453. ctx = context.WithValue(ctx, generateContextKey("context"), latest.Context)
  454. cmd.SetContext(ctx)
  455. return nil
  456. }
  457. func generateInteractive(cmd *cobra.Command, model string, wordWrap bool, format string) error {
  458. // load the model
  459. if err := generate(cmd, model, "", false, ""); err != nil {
  460. return err
  461. }
  462. usage := func() {
  463. fmt.Fprintln(os.Stderr, "Available Commands:")
  464. fmt.Fprintln(os.Stderr, " /set Set session variables")
  465. fmt.Fprintln(os.Stderr, " /show Show model information")
  466. fmt.Fprintln(os.Stderr, " /bye Exit")
  467. fmt.Fprintln(os.Stderr, " /?, /help Help for a command")
  468. fmt.Fprintln(os.Stderr, "")
  469. fmt.Fprintln(os.Stderr, "Use \"\"\" to begin a multi-line message.")
  470. fmt.Fprintln(os.Stderr, "")
  471. }
  472. usageSet := func() {
  473. fmt.Fprintln(os.Stderr, "Available Commands:")
  474. fmt.Fprintln(os.Stderr, " /set history Enable history")
  475. fmt.Fprintln(os.Stderr, " /set nohistory Disable history")
  476. fmt.Fprintln(os.Stderr, " /set wordwrap Enable wordwrap")
  477. fmt.Fprintln(os.Stderr, " /set nowordwrap Disable wordwrap")
  478. fmt.Fprintln(os.Stderr, " /set format json Enable JSON mode")
  479. fmt.Fprintln(os.Stderr, " /set noformat Disable formatting")
  480. fmt.Fprintln(os.Stderr, " /set verbose Show LLM stats")
  481. fmt.Fprintln(os.Stderr, " /set quiet Disable LLM stats")
  482. fmt.Fprintln(os.Stderr, "")
  483. }
  484. usageShow := func() {
  485. fmt.Fprintln(os.Stderr, "Available Commands:")
  486. fmt.Fprintln(os.Stderr, " /show license Show model license")
  487. fmt.Fprintln(os.Stderr, " /show modelfile Show Modelfile for this model")
  488. fmt.Fprintln(os.Stderr, " /show parameters Show parameters for this model")
  489. fmt.Fprintln(os.Stderr, " /show system Show system prompt")
  490. fmt.Fprintln(os.Stderr, " /show template Show prompt template")
  491. fmt.Fprintln(os.Stderr, "")
  492. }
  493. prompt := readline.Prompt{
  494. Prompt: ">>> ",
  495. AltPrompt: "... ",
  496. Placeholder: "Send a message (/? for help)",
  497. AltPlaceholder: `Use """ to end multi-line input`,
  498. }
  499. scanner, err := readline.New(prompt)
  500. if err != nil {
  501. return err
  502. }
  503. fmt.Print(readline.StartBracketedPaste)
  504. defer fmt.Printf(readline.EndBracketedPaste)
  505. var multiLineBuffer string
  506. for {
  507. line, err := scanner.Readline()
  508. switch {
  509. case errors.Is(err, io.EOF):
  510. fmt.Println()
  511. return nil
  512. case errors.Is(err, readline.ErrInterrupt):
  513. if line == "" {
  514. fmt.Println("\nUse Ctrl-D or /bye to exit.")
  515. }
  516. continue
  517. case err != nil:
  518. return err
  519. }
  520. line = strings.TrimSpace(line)
  521. switch {
  522. case scanner.Prompt.UseAlt:
  523. if strings.HasSuffix(line, `"""`) {
  524. scanner.Prompt.UseAlt = false
  525. multiLineBuffer += strings.TrimSuffix(line, `"""`)
  526. line = multiLineBuffer
  527. multiLineBuffer = ""
  528. } else {
  529. multiLineBuffer += line + " "
  530. continue
  531. }
  532. case strings.HasPrefix(line, `"""`):
  533. scanner.Prompt.UseAlt = true
  534. multiLineBuffer = strings.TrimPrefix(line, `"""`) + " "
  535. continue
  536. case strings.HasPrefix(line, "/list"):
  537. args := strings.Fields(line)
  538. if err := ListHandler(cmd, args[1:]); err != nil {
  539. return err
  540. }
  541. case strings.HasPrefix(line, "/set"):
  542. args := strings.Fields(line)
  543. if len(args) > 1 {
  544. switch args[1] {
  545. case "history":
  546. scanner.HistoryEnable()
  547. case "nohistory":
  548. scanner.HistoryDisable()
  549. case "wordwrap":
  550. wordWrap = true
  551. fmt.Println("Set 'wordwrap' mode.")
  552. case "nowordwrap":
  553. wordWrap = false
  554. fmt.Println("Set 'nowordwrap' mode.")
  555. case "verbose":
  556. cmd.Flags().Set("verbose", "true")
  557. fmt.Println("Set 'verbose' mode.")
  558. case "quiet":
  559. cmd.Flags().Set("verbose", "false")
  560. fmt.Println("Set 'quiet' mode.")
  561. case "format":
  562. if len(args) < 3 || args[2] != "json" {
  563. fmt.Println("Invalid or missing format. For 'json' mode use '/set format json'")
  564. } else {
  565. format = args[2]
  566. fmt.Printf("Set format to '%s' mode.\n", args[2])
  567. }
  568. case "noformat":
  569. format = ""
  570. fmt.Println("Disabled format.")
  571. default:
  572. fmt.Printf("Unknown command '/set %s'. Type /? for help\n", args[1])
  573. }
  574. } else {
  575. usageSet()
  576. }
  577. case strings.HasPrefix(line, "/show"):
  578. args := strings.Fields(line)
  579. if len(args) > 1 {
  580. client, err := api.ClientFromEnvironment()
  581. if err != nil {
  582. fmt.Println("error: couldn't connect to ollama server")
  583. return err
  584. }
  585. resp, err := client.Show(cmd.Context(), &api.ShowRequest{Name: model})
  586. if err != nil {
  587. fmt.Println("error: couldn't get model")
  588. return err
  589. }
  590. switch args[1] {
  591. case "license":
  592. if resp.License == "" {
  593. fmt.Print("No license was specified for this model.\n\n")
  594. } else {
  595. fmt.Println(resp.License)
  596. }
  597. case "modelfile":
  598. fmt.Println(resp.Modelfile)
  599. case "parameters":
  600. if resp.Parameters == "" {
  601. fmt.Print("No parameters were specified for this model.\n\n")
  602. } else {
  603. fmt.Println(resp.Parameters)
  604. }
  605. case "system":
  606. if resp.System == "" {
  607. fmt.Print("No system prompt was specified for this model.\n\n")
  608. } else {
  609. fmt.Println(resp.System)
  610. }
  611. case "template":
  612. if resp.Template == "" {
  613. fmt.Print("No prompt template was specified for this model.\n\n")
  614. } else {
  615. fmt.Println(resp.Template)
  616. }
  617. default:
  618. fmt.Printf("Unknown command '/show %s'. Type /? for help\n", args[1])
  619. }
  620. } else {
  621. usageShow()
  622. }
  623. case strings.HasPrefix(line, "/help"), strings.HasPrefix(line, "/?"):
  624. args := strings.Fields(line)
  625. if len(args) > 1 {
  626. switch args[1] {
  627. case "set", "/set":
  628. usageSet()
  629. case "show", "/show":
  630. usageShow()
  631. }
  632. } else {
  633. usage()
  634. }
  635. case line == "/exit", line == "/bye":
  636. return nil
  637. case strings.HasPrefix(line, "/"):
  638. args := strings.Fields(line)
  639. fmt.Printf("Unknown command '%s'. Type /? for help\n", args[0])
  640. }
  641. if len(line) > 0 && line[0] != '/' {
  642. if err := generate(cmd, model, line, wordWrap, format); err != nil {
  643. return err
  644. }
  645. }
  646. }
  647. }
  648. func RunServer(cmd *cobra.Command, _ []string) error {
  649. host, port, err := net.SplitHostPort(os.Getenv("OLLAMA_HOST"))
  650. if err != nil {
  651. host, port = "127.0.0.1", "11434"
  652. if ip := net.ParseIP(strings.Trim(os.Getenv("OLLAMA_HOST"), "[]")); ip != nil {
  653. host = ip.String()
  654. }
  655. }
  656. if err := initializeKeypair(); err != nil {
  657. return err
  658. }
  659. ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
  660. if err != nil {
  661. return err
  662. }
  663. var origins []string
  664. if o := os.Getenv("OLLAMA_ORIGINS"); o != "" {
  665. origins = strings.Split(o, ",")
  666. }
  667. return server.Serve(ln, origins)
  668. }
  669. func initializeKeypair() error {
  670. home, err := os.UserHomeDir()
  671. if err != nil {
  672. return err
  673. }
  674. privKeyPath := filepath.Join(home, ".ollama", "id_ed25519")
  675. pubKeyPath := filepath.Join(home, ".ollama", "id_ed25519.pub")
  676. _, err = os.Stat(privKeyPath)
  677. if os.IsNotExist(err) {
  678. fmt.Printf("Couldn't find '%s'. Generating new private key.\n", privKeyPath)
  679. _, privKey, err := ed25519.GenerateKey(rand.Reader)
  680. if err != nil {
  681. return err
  682. }
  683. privKeyBytes, err := format.OpenSSHPrivateKey(privKey, "")
  684. if err != nil {
  685. return err
  686. }
  687. err = os.MkdirAll(filepath.Dir(privKeyPath), 0o755)
  688. if err != nil {
  689. return fmt.Errorf("could not create directory %w", err)
  690. }
  691. err = os.WriteFile(privKeyPath, pem.EncodeToMemory(privKeyBytes), 0o600)
  692. if err != nil {
  693. return err
  694. }
  695. sshPrivateKey, err := ssh.NewSignerFromKey(privKey)
  696. if err != nil {
  697. return err
  698. }
  699. pubKeyData := ssh.MarshalAuthorizedKey(sshPrivateKey.PublicKey())
  700. err = os.WriteFile(pubKeyPath, pubKeyData, 0o644)
  701. if err != nil {
  702. return err
  703. }
  704. fmt.Printf("Your new public key is: \n\n%s\n", string(pubKeyData))
  705. }
  706. return nil
  707. }
  708. func startMacApp(client *api.Client) error {
  709. exe, err := os.Executable()
  710. if err != nil {
  711. return err
  712. }
  713. link, err := os.Readlink(exe)
  714. if err != nil {
  715. return err
  716. }
  717. if !strings.Contains(link, "Ollama.app") {
  718. return fmt.Errorf("could not find ollama app")
  719. }
  720. path := strings.Split(link, "Ollama.app")
  721. if err := exec.Command("/usr/bin/open", "-a", path[0]+"Ollama.app").Run(); err != nil {
  722. return err
  723. }
  724. // wait for the server to start
  725. timeout := time.After(5 * time.Second)
  726. tick := time.Tick(500 * time.Millisecond)
  727. for {
  728. select {
  729. case <-timeout:
  730. return errors.New("timed out waiting for server to start")
  731. case <-tick:
  732. if err := client.Heartbeat(context.Background()); err == nil {
  733. return nil // server has started
  734. }
  735. }
  736. }
  737. }
  738. func checkServerHeartbeat(_ *cobra.Command, _ []string) error {
  739. client, err := api.ClientFromEnvironment()
  740. if err != nil {
  741. return err
  742. }
  743. if err := client.Heartbeat(context.Background()); err != nil {
  744. if !strings.Contains(err.Error(), "connection refused") {
  745. return err
  746. }
  747. if runtime.GOOS == "darwin" {
  748. if err := startMacApp(client); err != nil {
  749. return fmt.Errorf("could not connect to ollama app, is it running?")
  750. }
  751. } else {
  752. return fmt.Errorf("could not connect to ollama server, run 'ollama serve' to start it")
  753. }
  754. }
  755. return nil
  756. }
  757. func NewCLI() *cobra.Command {
  758. log.SetFlags(log.LstdFlags | log.Lshortfile)
  759. rootCmd := &cobra.Command{
  760. Use: "ollama",
  761. Short: "Large language model runner",
  762. SilenceUsage: true,
  763. SilenceErrors: true,
  764. CompletionOptions: cobra.CompletionOptions{
  765. DisableDefaultCmd: true,
  766. },
  767. Version: version.Version,
  768. }
  769. cobra.EnableCommandSorting = false
  770. createCmd := &cobra.Command{
  771. Use: "create MODEL",
  772. Short: "Create a model from a Modelfile",
  773. Args: cobra.ExactArgs(1),
  774. PreRunE: checkServerHeartbeat,
  775. RunE: CreateHandler,
  776. }
  777. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
  778. showCmd := &cobra.Command{
  779. Use: "show MODEL",
  780. Short: "Show information for a model",
  781. Args: cobra.ExactArgs(1),
  782. PreRunE: checkServerHeartbeat,
  783. RunE: ShowHandler,
  784. }
  785. showCmd.Flags().Bool("license", false, "Show license of a model")
  786. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  787. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  788. showCmd.Flags().Bool("template", false, "Show template of a model")
  789. showCmd.Flags().Bool("system", false, "Show system prompt of a model")
  790. runCmd := &cobra.Command{
  791. Use: "run MODEL [PROMPT]",
  792. Short: "Run a model",
  793. Args: cobra.MinimumNArgs(1),
  794. PreRunE: checkServerHeartbeat,
  795. RunE: RunHandler,
  796. }
  797. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  798. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  799. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  800. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  801. serveCmd := &cobra.Command{
  802. Use: "serve",
  803. Aliases: []string{"start"},
  804. Short: "Start ollama",
  805. Args: cobra.ExactArgs(0),
  806. RunE: RunServer,
  807. }
  808. pullCmd := &cobra.Command{
  809. Use: "pull MODEL",
  810. Short: "Pull a model from a registry",
  811. Args: cobra.ExactArgs(1),
  812. PreRunE: checkServerHeartbeat,
  813. RunE: PullHandler,
  814. }
  815. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  816. pushCmd := &cobra.Command{
  817. Use: "push MODEL",
  818. Short: "Push a model to a registry",
  819. Args: cobra.ExactArgs(1),
  820. PreRunE: checkServerHeartbeat,
  821. RunE: PushHandler,
  822. }
  823. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  824. listCmd := &cobra.Command{
  825. Use: "list",
  826. Aliases: []string{"ls"},
  827. Short: "List models",
  828. PreRunE: checkServerHeartbeat,
  829. RunE: ListHandler,
  830. }
  831. copyCmd := &cobra.Command{
  832. Use: "cp SOURCE TARGET",
  833. Short: "Copy a model",
  834. Args: cobra.ExactArgs(2),
  835. PreRunE: checkServerHeartbeat,
  836. RunE: CopyHandler,
  837. }
  838. deleteCmd := &cobra.Command{
  839. Use: "rm MODEL [MODEL...]",
  840. Short: "Remove a model",
  841. Args: cobra.MinimumNArgs(1),
  842. PreRunE: checkServerHeartbeat,
  843. RunE: DeleteHandler,
  844. }
  845. rootCmd.AddCommand(
  846. serveCmd,
  847. createCmd,
  848. showCmd,
  849. runCmd,
  850. pullCmd,
  851. pushCmd,
  852. listCmd,
  853. copyCmd,
  854. deleteCmd,
  855. )
  856. return rootCmd
  857. }