config.go 10.4 KB
Newer Older
1
2
3
4
5
package envconfig

import (
	"fmt"
	"log/slog"
6
	"math"
Michael Yang's avatar
Michael Yang committed
7
	"net"
Michael Yang's avatar
host  
Michael Yang committed
8
	"net/url"
9
10
11
12
13
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
14
	"time"
15
16
)

Michael Yang's avatar
host  
Michael Yang committed
17
18
19
20
21
// Host returns the scheme and host. Host can be configured via the OLLAMA_HOST environment variable.
// Default is scheme "http" and host "127.0.0.1:11434"
func Host() *url.URL {
	defaultPort := "11434"

Michael Yang's avatar
Michael Yang committed
22
	s := strings.TrimSpace(Var("OLLAMA_HOST"))
Michael Yang's avatar
host  
Michael Yang committed
23
24
25
26
27
28
29
30
31
32
	scheme, hostport, ok := strings.Cut(s, "://")
	switch {
	case !ok:
		scheme, hostport = "http", s
	case scheme == "http":
		defaultPort = "80"
	case scheme == "https":
		defaultPort = "443"
	}

33
	hostport, path, _ := strings.Cut(hostport, "/")
Michael Yang's avatar
host  
Michael Yang committed
34
35
36
37
38
39
40
41
42
43
44
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		host, port = "127.0.0.1", defaultPort
		if ip := net.ParseIP(strings.Trim(hostport, "[]")); ip != nil {
			host = ip.String()
		} else if hostport != "" {
			host = hostport
		}
	}

	if n, err := strconv.ParseInt(port, 10, 32); err != nil || n > 65535 || n < 0 {
Michael Yang's avatar
Michael Yang committed
45
		slog.Warn("invalid port, using default", "port", port, "default", defaultPort)
46
		port = defaultPort
Michael Yang's avatar
host  
Michael Yang committed
47
48
49
50
51
	}

	return &url.URL{
		Scheme: scheme,
		Host:   net.JoinHostPort(host, port),
52
		Path:   path,
Michael Yang's avatar
host  
Michael Yang committed
53
54
55
	}
}

56
57
// AllowedOrigins returns a list of allowed origins. AllowedOrigins can be configured via the OLLAMA_ORIGINS environment variable.
func AllowedOrigins() (origins []string) {
Michael Yang's avatar
Michael Yang committed
58
	if s := Var("OLLAMA_ORIGINS"); s != "" {
Michael Yang's avatar
origins  
Michael Yang committed
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
		origins = strings.Split(s, ",")
	}

	for _, origin := range []string{"localhost", "127.0.0.1", "0.0.0.0"} {
		origins = append(origins,
			fmt.Sprintf("http://%s", origin),
			fmt.Sprintf("https://%s", origin),
			fmt.Sprintf("http://%s", net.JoinHostPort(origin, "*")),
			fmt.Sprintf("https://%s", net.JoinHostPort(origin, "*")),
		)
	}

	origins = append(origins,
		"app://*",
		"file://*",
		"tauri://*",
75
		"vscode-webview://*",
76
		"vscode-file://*",
Michael Yang's avatar
origins  
Michael Yang committed
77
78
79
80
81
	)

	return origins
}

Michael Yang's avatar
models  
Michael Yang committed
82
83
84
// Models returns the path to the models directory. Models directory can be configured via the OLLAMA_MODELS environment variable.
// Default is $HOME/.ollama/models
func Models() string {
Michael Yang's avatar
Michael Yang committed
85
	if s := Var("OLLAMA_MODELS"); s != "" {
Michael Yang's avatar
models  
Michael Yang committed
86
87
88
89
90
91
92
93
94
95
96
		return s
	}

	home, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	return filepath.Join(home, ".ollama", "models")
}

Michael Yang's avatar
Michael Yang committed
97
98
99
100
101
// KeepAlive returns the duration that models stay loaded in memory. KeepAlive can be configured via the OLLAMA_KEEP_ALIVE environment variable.
// Negative values are treated as infinite. Zero is treated as no keep alive.
// Default is 5 minutes.
func KeepAlive() (keepAlive time.Duration) {
	keepAlive = 5 * time.Minute
Michael Yang's avatar
Michael Yang committed
102
	if s := Var("OLLAMA_KEEP_ALIVE"); s != "" {
Michael Yang's avatar
Michael Yang committed
103
104
105
106
107
108
109
110
111
112
113
114
115
116
		if d, err := time.ParseDuration(s); err == nil {
			keepAlive = d
		} else if n, err := strconv.ParseInt(s, 10, 64); err == nil {
			keepAlive = time.Duration(n) * time.Second
		}
	}

	if keepAlive < 0 {
		return time.Duration(math.MaxInt64)
	}

	return keepAlive
}

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// LoadTimeout returns the duration for stall detection during model loads. LoadTimeout can be configured via the OLLAMA_LOAD_TIMEOUT environment variable.
// Zero or Negative values are treated as infinite.
// Default is 5 minutes.
func LoadTimeout() (loadTimeout time.Duration) {
	loadTimeout = 5 * time.Minute
	if s := Var("OLLAMA_LOAD_TIMEOUT"); s != "" {
		if d, err := time.ParseDuration(s); err == nil {
			loadTimeout = d
		} else if n, err := strconv.ParseInt(s, 10, 64); err == nil {
			loadTimeout = time.Duration(n) * time.Second
		}
	}

	if loadTimeout <= 0 {
		return time.Duration(math.MaxInt64)
	}

	return loadTimeout
}

Michael Yang's avatar
bool  
Michael Yang committed
137
138
func Bool(k string) func() bool {
	return func() bool {
Michael Yang's avatar
Michael Yang committed
139
		if s := Var(k); s != "" {
Michael Yang's avatar
bool  
Michael Yang committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
			b, err := strconv.ParseBool(s)
			if err != nil {
				return true
			}

			return b
		}

		return false
	}
}

var (
	// Debug enabled additional debug information.
	Debug = Bool("OLLAMA_DEBUG")
	// FlashAttention enables the experimental flash attention feature.
	FlashAttention = Bool("OLLAMA_FLASH_ATTENTION")
157
158
	// KvCacheType is the quantization type for the K/V cache.
	KvCacheType = String("OLLAMA_KV_CACHE_TYPE")
Michael Yang's avatar
bool  
Michael Yang committed
159
160
161
162
163
164
165
166
	// NoHistory disables readline history.
	NoHistory = Bool("OLLAMA_NOHISTORY")
	// NoPrune disables pruning of model blobs on startup.
	NoPrune = Bool("OLLAMA_NOPRUNE")
	// SchedSpread allows scheduling models across all GPUs.
	SchedSpread = Bool("OLLAMA_SCHED_SPREAD")
	// IntelGPU enables experimental Intel GPU detection.
	IntelGPU = Bool("OLLAMA_INTEL_GPU")
167
168
	// MultiUserCache optimizes prompt caching for multi-user scenarios
	MultiUserCache = Bool("OLLAMA_MULTIUSER_CACHE")
Jesse Gross's avatar
Jesse Gross committed
169
170
	// Enable the new Ollama engine
	NewEngine = Bool("OLLAMA_NEW_ENGINE")
171
	// ContextLength sets the default context length
172
	ContextLength = Uint("OLLAMA_CONTEXT_LENGTH", 4096)
Michael Yang's avatar
bool  
Michael Yang committed
173
174
)

Michael Yang's avatar
string  
Michael Yang committed
175
176
func String(s string) func() string {
	return func() string {
Michael Yang's avatar
Michael Yang committed
177
		return Var(s)
Michael Yang's avatar
string  
Michael Yang committed
178
179
180
181
182
183
184
185
186
187
188
189
190
	}
}

var (
	LLMLibrary = String("OLLAMA_LLM_LIBRARY")

	CudaVisibleDevices    = String("CUDA_VISIBLE_DEVICES")
	HipVisibleDevices     = String("HIP_VISIBLE_DEVICES")
	RocrVisibleDevices    = String("ROCR_VISIBLE_DEVICES")
	GpuDeviceOrdinal      = String("GPU_DEVICE_ORDINAL")
	HsaOverrideGfxVersion = String("HSA_OVERRIDE_GFX_VERSION")
)

Michael Yang's avatar
Michael Yang committed
191
192
193
194
195
196
197
func Uint(key string, defaultValue uint) func() uint {
	return func() uint {
		if s := Var(key); s != "" {
			if n, err := strconv.ParseUint(s, 10, 64); err != nil {
				slog.Warn("invalid environment variable, using default", "key", key, "value", s, "default", defaultValue)
			} else {
				return uint(n)
Michael Yang's avatar
int  
Michael Yang committed
198
199
200
			}
		}

Michael Yang's avatar
Michael Yang committed
201
		return defaultValue
Michael Yang's avatar
int  
Michael Yang committed
202
203
204
	}
}

205
var (
Michael Yang's avatar
Michael Yang committed
206
207
208
209
210
211
212
213
	// NumParallel sets the number of parallel model requests. NumParallel can be configured via the OLLAMA_NUM_PARALLEL environment variable.
	NumParallel = Uint("OLLAMA_NUM_PARALLEL", 0)
	// MaxRunners sets the maximum number of loaded models. MaxRunners can be configured via the OLLAMA_MAX_LOADED_MODELS environment variable.
	MaxRunners = Uint("OLLAMA_MAX_LOADED_MODELS", 0)
	// MaxQueue sets the maximum number of queued requests. MaxQueue can be configured via the OLLAMA_MAX_QUEUE environment variable.
	MaxQueue = Uint("OLLAMA_MAX_QUEUE", 512)
	// MaxVRAM sets a maximum VRAM override in bytes. MaxVRAM can be configured via the OLLAMA_MAX_VRAM environment variable.
	MaxVRAM = Uint("OLLAMA_MAX_VRAM", 0)
214
215
)

216
217
218
219
220
221
222
223
224
225
226
227
228
229
func Uint64(key string, defaultValue uint64) func() uint64 {
	return func() uint64 {
		if s := Var(key); s != "" {
			if n, err := strconv.ParseUint(s, 10, 64); err != nil {
				slog.Warn("invalid environment variable, using default", "key", key, "value", s, "default", defaultValue)
			} else {
				return n
			}
		}

		return defaultValue
	}
}

230
231
// Set aside VRAM per GPU
var GpuOverhead = Uint64("OLLAMA_GPU_OVERHEAD", 0)
232

233
234
235
236
237
238
239
type EnvVar struct {
	Name        string
	Value       any
	Description string
}

func AsMap() map[string]EnvVar {
240
	ret := map[string]EnvVar{
Michael Yang's avatar
Michael Yang committed
241
		"OLLAMA_DEBUG":             {"OLLAMA_DEBUG", Debug(), "Show additional debug information (e.g. OLLAMA_DEBUG=1)"},
Michael Yang's avatar
bool  
Michael Yang committed
242
		"OLLAMA_FLASH_ATTENTION":   {"OLLAMA_FLASH_ATTENTION", FlashAttention(), "Enabled flash attention"},
243
		"OLLAMA_KV_CACHE_TYPE":     {"OLLAMA_KV_CACHE_TYPE", KvCacheType(), "Quantization type for the K/V cache (default: f16)"},
244
		"OLLAMA_GPU_OVERHEAD":      {"OLLAMA_GPU_OVERHEAD", GpuOverhead(), "Reserve a portion of VRAM per GPU (bytes)"},
Michael Yang's avatar
host  
Michael Yang committed
245
		"OLLAMA_HOST":              {"OLLAMA_HOST", Host(), "IP Address for the ollama server (default 127.0.0.1:11434)"},
Michael Yang's avatar
Michael Yang committed
246
		"OLLAMA_KEEP_ALIVE":        {"OLLAMA_KEEP_ALIVE", KeepAlive(), "The duration that models stay loaded in memory (default \"5m\")"},
Michael Yang's avatar
string  
Michael Yang committed
247
		"OLLAMA_LLM_LIBRARY":       {"OLLAMA_LLM_LIBRARY", LLMLibrary(), "Set LLM library to bypass autodetection"},
248
		"OLLAMA_LOAD_TIMEOUT":      {"OLLAMA_LOAD_TIMEOUT", LoadTimeout(), "How long to allow model loads to stall before giving up (default \"5m\")"},
Michael Yang's avatar
int  
Michael Yang committed
249
250
		"OLLAMA_MAX_LOADED_MODELS": {"OLLAMA_MAX_LOADED_MODELS", MaxRunners(), "Maximum number of loaded models per GPU"},
		"OLLAMA_MAX_QUEUE":         {"OLLAMA_MAX_QUEUE", MaxQueue(), "Maximum number of queued requests"},
Michael Yang's avatar
models  
Michael Yang committed
251
		"OLLAMA_MODELS":            {"OLLAMA_MODELS", Models(), "The path to the models directory"},
Michael Yang's avatar
bool  
Michael Yang committed
252
253
		"OLLAMA_NOHISTORY":         {"OLLAMA_NOHISTORY", NoHistory(), "Do not preserve readline history"},
		"OLLAMA_NOPRUNE":           {"OLLAMA_NOPRUNE", NoPrune(), "Do not prune model blobs on startup"},
Michael Yang's avatar
int  
Michael Yang committed
254
		"OLLAMA_NUM_PARALLEL":      {"OLLAMA_NUM_PARALLEL", NumParallel(), "Maximum number of parallel requests"},
255
		"OLLAMA_ORIGINS":           {"OLLAMA_ORIGINS", AllowedOrigins(), "A comma separated list of allowed origins"},
Michael Yang's avatar
bool  
Michael Yang committed
256
		"OLLAMA_SCHED_SPREAD":      {"OLLAMA_SCHED_SPREAD", SchedSpread(), "Always schedule model across all GPUs"},
257
		"OLLAMA_MULTIUSER_CACHE":   {"OLLAMA_MULTIUSER_CACHE", MultiUserCache(), "Optimize prompt caching for multi-user scenarios"},
258
		"OLLAMA_CONTEXT_LENGTH":    {"OLLAMA_CONTEXT_LENGTH", ContextLength(), "Context length to use unless otherwise specified (default: 4096)"},
Jesse Gross's avatar
Jesse Gross committed
259
		"OLLAMA_NEW_ENGINE":        {"OLLAMA_NEW_ENGINE", NewEngine(), "Enable the new Ollama engine"},
Michael Yang's avatar
Michael Yang committed
260
261
262
263
264
265
266
267
268
269
270
271

		// Informational
		"HTTP_PROXY":  {"HTTP_PROXY", String("HTTP_PROXY")(), "HTTP proxy"},
		"HTTPS_PROXY": {"HTTPS_PROXY", String("HTTPS_PROXY")(), "HTTPS proxy"},
		"NO_PROXY":    {"NO_PROXY", String("NO_PROXY")(), "No proxy"},
	}

	if runtime.GOOS != "windows" {
		// Windows environment variables are case-insensitive so there's no need to duplicate them
		ret["http_proxy"] = EnvVar{"http_proxy", String("http_proxy")(), "HTTP proxy"}
		ret["https_proxy"] = EnvVar{"https_proxy", String("https_proxy")(), "HTTPS proxy"}
		ret["no_proxy"] = EnvVar{"no_proxy", String("no_proxy")(), "No proxy"}
272
	}
Michael Yang's avatar
Michael Yang committed
273

274
	if runtime.GOOS != "darwin" {
Michael Yang's avatar
string  
Michael Yang committed
275
		ret["CUDA_VISIBLE_DEVICES"] = EnvVar{"CUDA_VISIBLE_DEVICES", CudaVisibleDevices(), "Set which NVIDIA devices are visible"}
276
277
278
		ret["HIP_VISIBLE_DEVICES"] = EnvVar{"HIP_VISIBLE_DEVICES", HipVisibleDevices(), "Set which AMD devices are visible by numeric ID"}
		ret["ROCR_VISIBLE_DEVICES"] = EnvVar{"ROCR_VISIBLE_DEVICES", RocrVisibleDevices(), "Set which AMD devices are visible by UUID or numeric ID"}
		ret["GPU_DEVICE_ORDINAL"] = EnvVar{"GPU_DEVICE_ORDINAL", GpuDeviceOrdinal(), "Set which AMD devices are visible by numeric ID"}
Michael Yang's avatar
string  
Michael Yang committed
279
		ret["HSA_OVERRIDE_GFX_VERSION"] = EnvVar{"HSA_OVERRIDE_GFX_VERSION", HsaOverrideGfxVersion(), "Override the gfx used for all detected AMD GPUs"}
Michael Yang's avatar
bool  
Michael Yang committed
280
		ret["OLLAMA_INTEL_GPU"] = EnvVar{"OLLAMA_INTEL_GPU", IntelGPU(), "Enable experimental Intel GPU detection"}
281
	}
Michael Yang's avatar
Michael Yang committed
282

283
	return ret
284
285
}

286
287
288
289
290
291
292
293
func Values() map[string]string {
	vals := make(map[string]string)
	for k, v := range AsMap() {
		vals[k] = fmt.Sprintf("%v", v.Value)
	}
	return vals
}

Michael Yang's avatar
Michael Yang committed
294
295
296
// Var returns an environment variable stripped of leading and trailing quotes or spaces
func Var(key string) string {
	return strings.Trim(strings.TrimSpace(os.Getenv(key)), "\"'")
297
}