cmd.go 21 KB

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