cmd.go 23 KB

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