models.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package server
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "os"
  9. "path/filepath"
  10. "strconv"
  11. "github.com/jmorganca/ollama/api"
  12. )
  13. const directoryURL = "https://ollama.ai/api/models"
  14. type Model struct {
  15. Name string `json:"name"`
  16. ModelPath string
  17. Prompt string
  18. Options api.Options
  19. DisplayName string `json:"display_name"`
  20. Parameters string `json:"parameters"`
  21. URL string `json:"url"`
  22. ShortDescription string `json:"short_description"`
  23. Description string `json:"description"`
  24. PublishedBy string `json:"published_by"`
  25. OriginalAuthor string `json:"original_author"`
  26. OriginalURL string `json:"original_url"`
  27. License string `json:"license"`
  28. }
  29. func (m *Model) FullName() string {
  30. home, err := os.UserHomeDir()
  31. if err != nil {
  32. panic(err)
  33. }
  34. return filepath.Join(home, ".ollama", "models", m.Name+".bin")
  35. }
  36. func (m *Model) TempFile() string {
  37. fullName := m.FullName()
  38. return filepath.Join(
  39. filepath.Dir(fullName),
  40. fmt.Sprintf(".%s.part", filepath.Base(fullName)),
  41. )
  42. }
  43. func getRemote(model string) (*Model, error) {
  44. // resolve the model download from our directory
  45. resp, err := http.Get(directoryURL)
  46. if err != nil {
  47. return nil, fmt.Errorf("failed to get directory: %w", err)
  48. }
  49. defer resp.Body.Close()
  50. body, err := io.ReadAll(resp.Body)
  51. if err != nil {
  52. return nil, fmt.Errorf("failed to read directory: %w", err)
  53. }
  54. var models []Model
  55. err = json.Unmarshal(body, &models)
  56. if err != nil {
  57. return nil, fmt.Errorf("failed to parse directory: %w", err)
  58. }
  59. for _, m := range models {
  60. if m.Name == model {
  61. return &m, nil
  62. }
  63. }
  64. return nil, fmt.Errorf("model not found in directory: %s", model)
  65. }
  66. func saveModel(model *Model, fn func(total, completed int64)) error {
  67. // this models cache directory is created by the server on startup
  68. client := &http.Client{}
  69. req, err := http.NewRequest("GET", model.URL, nil)
  70. if err != nil {
  71. return fmt.Errorf("failed to download model: %w", err)
  72. }
  73. var size int64
  74. // completed file doesn't exist, check partial file
  75. fi, err := os.Stat(model.TempFile())
  76. switch {
  77. case errors.Is(err, os.ErrNotExist):
  78. // noop, file doesn't exist so create it
  79. case err != nil:
  80. return fmt.Errorf("stat: %w", err)
  81. default:
  82. size = fi.Size()
  83. }
  84. req.Header.Add("Range", fmt.Sprintf("bytes=%d-", size))
  85. resp, err := client.Do(req)
  86. if err != nil {
  87. return fmt.Errorf("failed to download model: %w", err)
  88. }
  89. defer resp.Body.Close()
  90. if resp.StatusCode >= 400 {
  91. return fmt.Errorf("failed to download model: %s", resp.Status)
  92. }
  93. out, err := os.OpenFile(model.TempFile(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
  94. if err != nil {
  95. panic(err)
  96. }
  97. defer out.Close()
  98. remaining, _ := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
  99. completed := size
  100. total := remaining + completed
  101. for {
  102. fn(total, completed)
  103. if completed >= total {
  104. return os.Rename(model.TempFile(), model.FullName())
  105. }
  106. n, err := io.CopyN(out, resp.Body, 8192)
  107. if err != nil && !errors.Is(err, io.EOF) {
  108. return err
  109. }
  110. completed += n
  111. }
  112. }