config.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package envconfig
  2. import (
  3. "errors"
  4. "fmt"
  5. "log/slog"
  6. "net"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "strconv"
  11. "strings"
  12. )
  13. type OllamaHost struct {
  14. Scheme string
  15. Host string
  16. Port string
  17. }
  18. func (o OllamaHost) String() string {
  19. return fmt.Sprintf("%s://%s:%s", o.Scheme, o.Host, o.Port)
  20. }
  21. var ErrInvalidHostPort = errors.New("invalid port specified in OLLAMA_HOST")
  22. var (
  23. // Set via OLLAMA_ORIGINS in the environment
  24. AllowOrigins []string
  25. // Set via OLLAMA_DEBUG in the environment
  26. Debug bool
  27. // Experimental flash attention
  28. FlashAttention bool
  29. // Set via OLLAMA_KEEP_ALIVE in the environment
  30. KeepAlive string
  31. // Set via OLLAMA_LLM_LIBRARY in the environment
  32. LLMLibrary string
  33. // Set via OLLAMA_MAX_LOADED_MODELS in the environment
  34. MaxRunners int
  35. // Set via OLLAMA_MAX_QUEUE in the environment
  36. MaxQueuedRequests int
  37. // Set via OLLAMA_MAX_VRAM in the environment
  38. MaxVRAM uint64
  39. // Set via OLLAMA_NOHISTORY in the environment
  40. NoHistory bool
  41. // Set via OLLAMA_NOPRUNE in the environment
  42. NoPrune bool
  43. // Set via OLLAMA_NUM_PARALLEL in the environment
  44. NumParallel int
  45. // Set via OLLAMA_HOST in the environment
  46. Host *OllamaHost
  47. // Set via OLLAMA_RUNNERS_DIR in the environment
  48. RunnersDir string
  49. // Set via OLLAMA_TMPDIR in the environment
  50. TmpDir string
  51. )
  52. type EnvVar struct {
  53. Name string
  54. Value any
  55. Description string
  56. }
  57. func AsMap() map[string]EnvVar {
  58. return map[string]EnvVar{
  59. "OLLAMA_DEBUG": {"OLLAMA_DEBUG", Debug, "Show additional debug information (e.g. OLLAMA_DEBUG=1)"},
  60. "OLLAMA_FLASH_ATTENTION": {"OLLAMA_FLASH_ATTENTION", FlashAttention, "Enabled flash attention"},
  61. "OLLAMA_HOST": {"OLLAMA_HOST", Host, "IP Address for the ollama server (default 127.0.0.1:11434)"},
  62. "OLLAMA_KEEP_ALIVE": {"OLLAMA_KEEP_ALIVE", KeepAlive, "The duration that models stay loaded in memory (default \"5m\")"},
  63. "OLLAMA_LLM_LIBRARY": {"OLLAMA_LLM_LIBRARY", LLMLibrary, "Set LLM library to bypass autodetection"},
  64. "OLLAMA_MAX_LOADED_MODELS": {"OLLAMA_MAX_LOADED_MODELS", MaxRunners, "Maximum number of loaded models (default 1)"},
  65. "OLLAMA_MAX_QUEUE": {"OLLAMA_MAX_QUEUE", MaxQueuedRequests, "Maximum number of queued requests"},
  66. "OLLAMA_MAX_VRAM": {"OLLAMA_MAX_VRAM", MaxVRAM, "Maximum VRAM"},
  67. "OLLAMA_MODELS": {"OLLAMA_MODELS", "", "The path to the models directory"},
  68. "OLLAMA_NOHISTORY": {"OLLAMA_NOHISTORY", NoHistory, "Do not preserve readline history"},
  69. "OLLAMA_NOPRUNE": {"OLLAMA_NOPRUNE", NoPrune, "Do not prune model blobs on startup"},
  70. "OLLAMA_NUM_PARALLEL": {"OLLAMA_NUM_PARALLEL", NumParallel, "Maximum number of parallel requests (default 1)"},
  71. "OLLAMA_ORIGINS": {"OLLAMA_ORIGINS", AllowOrigins, "A comma separated list of allowed origins"},
  72. "OLLAMA_RUNNERS_DIR": {"OLLAMA_RUNNERS_DIR", RunnersDir, "Location for runners"},
  73. "OLLAMA_TMPDIR": {"OLLAMA_TMPDIR", TmpDir, "Location for temporary files"},
  74. }
  75. }
  76. func Values() map[string]string {
  77. vals := make(map[string]string)
  78. for k, v := range AsMap() {
  79. vals[k] = fmt.Sprintf("%v", v.Value)
  80. }
  81. return vals
  82. }
  83. var defaultAllowOrigins = []string{
  84. "localhost",
  85. "127.0.0.1",
  86. "0.0.0.0",
  87. }
  88. // Clean quotes and spaces from the value
  89. func clean(key string) string {
  90. return strings.Trim(os.Getenv(key), "\"' ")
  91. }
  92. func init() {
  93. // default values
  94. NumParallel = 1
  95. MaxRunners = 1
  96. MaxQueuedRequests = 512
  97. LoadConfig()
  98. }
  99. func LoadConfig() {
  100. if debug := clean("OLLAMA_DEBUG"); debug != "" {
  101. d, err := strconv.ParseBool(debug)
  102. if err == nil {
  103. Debug = d
  104. } else {
  105. Debug = true
  106. }
  107. }
  108. if fa := clean("OLLAMA_FLASH_ATTENTION"); fa != "" {
  109. d, err := strconv.ParseBool(fa)
  110. if err == nil {
  111. FlashAttention = d
  112. }
  113. }
  114. RunnersDir = clean("OLLAMA_RUNNERS_DIR")
  115. if runtime.GOOS == "windows" && RunnersDir == "" {
  116. // On Windows we do not carry the payloads inside the main executable
  117. appExe, err := os.Executable()
  118. if err != nil {
  119. slog.Error("failed to lookup executable path", "error", err)
  120. }
  121. cwd, err := os.Getwd()
  122. if err != nil {
  123. slog.Error("failed to lookup working directory", "error", err)
  124. }
  125. var paths []string
  126. for _, root := range []string{filepath.Dir(appExe), cwd} {
  127. paths = append(paths,
  128. root,
  129. filepath.Join(root, "windows-"+runtime.GOARCH),
  130. filepath.Join(root, "dist", "windows-"+runtime.GOARCH),
  131. )
  132. }
  133. // Try a few variations to improve developer experience when building from source in the local tree
  134. for _, p := range paths {
  135. candidate := filepath.Join(p, "ollama_runners")
  136. _, err := os.Stat(candidate)
  137. if err == nil {
  138. RunnersDir = candidate
  139. break
  140. }
  141. }
  142. if RunnersDir == "" {
  143. slog.Error("unable to locate llm runner directory. Set OLLAMA_RUNNERS_DIR to the location of 'ollama_runners'")
  144. }
  145. }
  146. TmpDir = clean("OLLAMA_TMPDIR")
  147. userLimit := clean("OLLAMA_MAX_VRAM")
  148. if userLimit != "" {
  149. avail, err := strconv.ParseUint(userLimit, 10, 64)
  150. if err != nil {
  151. slog.Error("invalid setting, ignoring", "OLLAMA_MAX_VRAM", userLimit, "error", err)
  152. } else {
  153. MaxVRAM = avail
  154. }
  155. }
  156. LLMLibrary = clean("OLLAMA_LLM_LIBRARY")
  157. if onp := clean("OLLAMA_NUM_PARALLEL"); onp != "" {
  158. val, err := strconv.Atoi(onp)
  159. if err != nil || val <= 0 {
  160. slog.Error("invalid setting must be greater than zero", "OLLAMA_NUM_PARALLEL", onp, "error", err)
  161. } else {
  162. NumParallel = val
  163. }
  164. }
  165. if nohistory := clean("OLLAMA_NOHISTORY"); nohistory != "" {
  166. NoHistory = true
  167. }
  168. if noprune := clean("OLLAMA_NOPRUNE"); noprune != "" {
  169. NoPrune = true
  170. }
  171. if origins := clean("OLLAMA_ORIGINS"); origins != "" {
  172. AllowOrigins = strings.Split(origins, ",")
  173. }
  174. for _, allowOrigin := range defaultAllowOrigins {
  175. AllowOrigins = append(AllowOrigins,
  176. fmt.Sprintf("http://%s", allowOrigin),
  177. fmt.Sprintf("https://%s", allowOrigin),
  178. fmt.Sprintf("http://%s", net.JoinHostPort(allowOrigin, "*")),
  179. fmt.Sprintf("https://%s", net.JoinHostPort(allowOrigin, "*")),
  180. )
  181. }
  182. AllowOrigins = append(AllowOrigins,
  183. "app://*",
  184. "file://*",
  185. "tauri://*",
  186. )
  187. maxRunners := clean("OLLAMA_MAX_LOADED_MODELS")
  188. if maxRunners != "" {
  189. m, err := strconv.Atoi(maxRunners)
  190. if err != nil {
  191. slog.Error("invalid setting", "OLLAMA_MAX_LOADED_MODELS", maxRunners, "error", err)
  192. } else {
  193. MaxRunners = m
  194. }
  195. }
  196. if onp := os.Getenv("OLLAMA_MAX_QUEUE"); onp != "" {
  197. p, err := strconv.Atoi(onp)
  198. if err != nil || p <= 0 {
  199. slog.Error("invalid setting", "OLLAMA_MAX_QUEUE", onp, "error", err)
  200. } else {
  201. MaxQueuedRequests = p
  202. }
  203. }
  204. KeepAlive = clean("OLLAMA_KEEP_ALIVE")
  205. var err error
  206. Host, err = getOllamaHost()
  207. if err != nil {
  208. slog.Error("invalid setting", "OLLAMA_HOST", Host, "error", err, "using default port", Host.Port)
  209. }
  210. }
  211. func getOllamaHost() (*OllamaHost, error) {
  212. defaultPort := "11434"
  213. hostVar := os.Getenv("OLLAMA_HOST")
  214. hostVar = strings.TrimSpace(strings.Trim(strings.TrimSpace(hostVar), "\"'"))
  215. scheme, hostport, ok := strings.Cut(hostVar, "://")
  216. switch {
  217. case !ok:
  218. scheme, hostport = "http", hostVar
  219. case scheme == "http":
  220. defaultPort = "80"
  221. case scheme == "https":
  222. defaultPort = "443"
  223. }
  224. // trim trailing slashes
  225. hostport = strings.TrimRight(hostport, "/")
  226. host, port, err := net.SplitHostPort(hostport)
  227. if err != nil {
  228. host, port = "127.0.0.1", defaultPort
  229. if ip := net.ParseIP(strings.Trim(hostport, "[]")); ip != nil {
  230. host = ip.String()
  231. } else if hostport != "" {
  232. host = hostport
  233. }
  234. }
  235. if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 0 {
  236. return &OllamaHost{
  237. Scheme: scheme,
  238. Host: host,
  239. Port: defaultPort,
  240. }, ErrInvalidHostPort
  241. }
  242. return &OllamaHost{
  243. Scheme: scheme,
  244. Host: host,
  245. Port: port,
  246. }, nil
  247. }