modelpath.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package server
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/fs"
  6. "net/url"
  7. "os"
  8. "path/filepath"
  9. "regexp"
  10. "strings"
  11. "github.com/ollama/ollama/envconfig"
  12. "github.com/ollama/ollama/types/model"
  13. )
  14. type ModelPath struct {
  15. ProtocolScheme string
  16. Registry string
  17. Namespace string
  18. Repository string
  19. Tag string
  20. }
  21. const (
  22. DefaultRegistry = "registry.ollama.ai"
  23. DefaultNamespace = "library"
  24. DefaultTag = "latest"
  25. DefaultProtocolScheme = "https"
  26. )
  27. var (
  28. ErrInvalidImageFormat = errors.New("invalid image format")
  29. ErrInvalidProtocol = errors.New("invalid protocol scheme")
  30. ErrInsecureProtocol = errors.New("insecure protocol http")
  31. ErrInvalidDigestFormat = errors.New("invalid digest format")
  32. )
  33. func ParseModelPath(name string) ModelPath {
  34. mp := ModelPath{
  35. ProtocolScheme: DefaultProtocolScheme,
  36. Registry: DefaultRegistry,
  37. Namespace: DefaultNamespace,
  38. Repository: "",
  39. Tag: DefaultTag,
  40. }
  41. before, after, found := strings.Cut(name, "://")
  42. if found {
  43. mp.ProtocolScheme = before
  44. name = after
  45. }
  46. name = strings.ReplaceAll(name, string(os.PathSeparator), "/")
  47. parts := strings.Split(name, "/")
  48. switch len(parts) {
  49. case 3:
  50. mp.Registry = parts[0]
  51. mp.Namespace = parts[1]
  52. mp.Repository = parts[2]
  53. case 2:
  54. mp.Namespace = parts[0]
  55. mp.Repository = parts[1]
  56. case 1:
  57. mp.Repository = parts[0]
  58. }
  59. if repo, tag, found := strings.Cut(mp.Repository, ":"); found {
  60. mp.Repository = repo
  61. mp.Tag = tag
  62. }
  63. return mp
  64. }
  65. var errModelPathInvalid = errors.New("invalid model path")
  66. func (mp ModelPath) GetNamespaceRepository() string {
  67. return fmt.Sprintf("%s/%s", mp.Namespace, mp.Repository)
  68. }
  69. func (mp ModelPath) GetFullTagname() string {
  70. return fmt.Sprintf("%s/%s/%s:%s", mp.Registry, mp.Namespace, mp.Repository, mp.Tag)
  71. }
  72. func (mp ModelPath) GetShortTagname() string {
  73. if mp.Registry == DefaultRegistry {
  74. if mp.Namespace == DefaultNamespace {
  75. return fmt.Sprintf("%s:%s", mp.Repository, mp.Tag)
  76. }
  77. return fmt.Sprintf("%s/%s:%s", mp.Namespace, mp.Repository, mp.Tag)
  78. }
  79. return fmt.Sprintf("%s/%s/%s:%s", mp.Registry, mp.Namespace, mp.Repository, mp.Tag)
  80. }
  81. // GetManifestPath returns the path to the manifest file for the given model path, it is up to the caller to create the directory if it does not exist.
  82. func (mp ModelPath) GetManifestPath() (string, error) {
  83. name := model.Name{
  84. Host: mp.Registry,
  85. Namespace: mp.Namespace,
  86. Model: mp.Repository,
  87. Tag: mp.Tag,
  88. }
  89. if !name.IsValid() {
  90. return "", fs.ErrNotExist
  91. }
  92. return filepath.Join(envconfig.Models(), "manifests", name.Filepath()), nil
  93. }
  94. func (mp ModelPath) BaseURL() *url.URL {
  95. return &url.URL{
  96. Scheme: mp.ProtocolScheme,
  97. Host: mp.Registry,
  98. }
  99. }
  100. func GetManifestPath() (string, error) {
  101. path := filepath.Join(envconfig.Models(), "manifests")
  102. if err := os.MkdirAll(path, 0o755); err != nil {
  103. return "", err
  104. }
  105. return path, nil
  106. }
  107. func GetBlobsPath(digest string) (string, error) {
  108. // only accept actual sha256 digests
  109. pattern := "^sha256[:-][0-9a-fA-F]{64}$"
  110. re := regexp.MustCompile(pattern)
  111. if digest != "" && !re.MatchString(digest) {
  112. return "", ErrInvalidDigestFormat
  113. }
  114. digest = strings.ReplaceAll(digest, ":", "-")
  115. path := filepath.Join(envconfig.Models(), "blobs", digest)
  116. dirPath := filepath.Dir(path)
  117. if digest == "" {
  118. dirPath = path
  119. }
  120. if err := os.MkdirAll(dirPath, 0o755); err != nil {
  121. return "", err
  122. }
  123. return path, nil
  124. }