cmd.go 23 KB

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