Skip to content

Commit c69bc19

Browse files
authored
move OLLAMA_HOST to envconfig (#5009)
1 parent bba5d17 commit c69bc19

6 files changed

Lines changed: 119 additions & 103 deletions

File tree

api/client.go

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@ import (
2323
"net"
2424
"net/http"
2525
"net/url"
26-
"os"
2726
"runtime"
28-
"strconv"
29-
"strings"
3027

28+
"github.com/ollama/ollama/envconfig"
3129
"github.com/ollama/ollama/format"
3230
"github.com/ollama/ollama/version"
3331
)
@@ -65,10 +63,7 @@ func checkError(resp *http.Response, body []byte) error {
6563
// If the variable is not specified, a default ollama host and port will be
6664
// used.
6765
func ClientFromEnvironment() (*Client, error) {
68-
ollamaHost, err := GetOllamaHost()
69-
if err != nil {
70-
return nil, err
71-
}
66+
ollamaHost := envconfig.Host
7267

7368
return &Client{
7469
base: &url.URL{
@@ -79,52 +74,6 @@ func ClientFromEnvironment() (*Client, error) {
7974
}, nil
8075
}
8176

82-
type OllamaHost struct {
83-
Scheme string
84-
Host string
85-
Port string
86-
}
87-
88-
func GetOllamaHost() (OllamaHost, error) {
89-
defaultPort := "11434"
90-
91-
hostVar := os.Getenv("OLLAMA_HOST")
92-
hostVar = strings.TrimSpace(strings.Trim(strings.TrimSpace(hostVar), "\"'"))
93-
94-
scheme, hostport, ok := strings.Cut(hostVar, "://")
95-
switch {
96-
case !ok:
97-
scheme, hostport = "http", hostVar
98-
case scheme == "http":
99-
defaultPort = "80"
100-
case scheme == "https":
101-
defaultPort = "443"
102-
}
103-
104-
// trim trailing slashes
105-
hostport = strings.TrimRight(hostport, "/")
106-
107-
host, port, err := net.SplitHostPort(hostport)
108-
if err != nil {
109-
host, port = "127.0.0.1", defaultPort
110-
if ip := net.ParseIP(strings.Trim(hostport, "[]")); ip != nil {
111-
host = ip.String()
112-
} else if hostport != "" {
113-
host = hostport
114-
}
115-
}
116-
117-
if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 0 {
118-
return OllamaHost{}, ErrInvalidHostPort
119-
}
120-
121-
return OllamaHost{
122-
Scheme: scheme,
123-
Host: host,
124-
Port: port,
125-
}, nil
126-
}
127-
12877
func NewClient(base *url.URL, http *http.Client) *Client {
12978
return &Client{
13079
base: base,

api/client_test.go

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package api
22

33
import (
4-
"fmt"
5-
"net"
64
"testing"
75

8-
"github.com/stretchr/testify/assert"
6+
"github.com/ollama/ollama/envconfig"
97
)
108

119
func TestClientFromEnvironment(t *testing.T) {
@@ -35,6 +33,7 @@ func TestClientFromEnvironment(t *testing.T) {
3533
for k, v := range testCases {
3634
t.Run(k, func(t *testing.T) {
3735
t.Setenv("OLLAMA_HOST", v.value)
36+
envconfig.LoadConfig()
3837

3938
client, err := ClientFromEnvironment()
4039
if err != v.err {
@@ -46,40 +45,4 @@ func TestClientFromEnvironment(t *testing.T) {
4645
}
4746
})
4847
}
49-
50-
hostTestCases := map[string]*testCase{
51-
"empty": {value: "", expect: "127.0.0.1:11434"},
52-
"only address": {value: "1.2.3.4", expect: "1.2.3.4:11434"},
53-
"only port": {value: ":1234", expect: ":1234"},
54-
"address and port": {value: "1.2.3.4:1234", expect: "1.2.3.4:1234"},
55-
"hostname": {value: "example.com", expect: "example.com:11434"},
56-
"hostname and port": {value: "example.com:1234", expect: "example.com:1234"},
57-
"zero port": {value: ":0", expect: ":0"},
58-
"too large port": {value: ":66000", err: ErrInvalidHostPort},
59-
"too small port": {value: ":-1", err: ErrInvalidHostPort},
60-
"ipv6 localhost": {value: "[::1]", expect: "[::1]:11434"},
61-
"ipv6 world open": {value: "[::]", expect: "[::]:11434"},
62-
"ipv6 no brackets": {value: "::1", expect: "[::1]:11434"},
63-
"ipv6 + port": {value: "[::1]:1337", expect: "[::1]:1337"},
64-
"extra space": {value: " 1.2.3.4 ", expect: "1.2.3.4:11434"},
65-
"extra quotes": {value: "\"1.2.3.4\"", expect: "1.2.3.4:11434"},
66-
"extra space+quotes": {value: " \" 1.2.3.4 \" ", expect: "1.2.3.4:11434"},
67-
"extra single quotes": {value: "'1.2.3.4'", expect: "1.2.3.4:11434"},
68-
}
69-
70-
for k, v := range hostTestCases {
71-
t.Run(k, func(t *testing.T) {
72-
t.Setenv("OLLAMA_HOST", v.value)
73-
74-
oh, err := GetOllamaHost()
75-
if err != v.err {
76-
t.Fatalf("expected %s, got %s", v.err, err)
77-
}
78-
79-
if err == nil {
80-
host := net.JoinHostPort(oh.Host, oh.Port)
81-
assert.Equal(t, v.expect, host, fmt.Sprintf("%s: expected %s, got %s", k, v.expect, host))
82-
}
83-
})
84-
}
8548
}

api/types.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package api
22

33
import (
44
"encoding/json"
5-
"errors"
65
"fmt"
76
"log/slog"
87
"math"
@@ -377,8 +376,6 @@ func (m *Metrics) Summary() {
377376
}
378377
}
379378

380-
var ErrInvalidHostPort = errors.New("invalid port specified in OLLAMA_HOST")
381-
382379
func (opts *Options) FromMap(m map[string]interface{}) error {
383380
valueOpts := reflect.ValueOf(opts).Elem() // names of the fields in the options struct
384381
typeOpts := reflect.TypeOf(opts).Elem() // types of the fields in the options struct

cmd/cmd.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -960,17 +960,11 @@ func generate(cmd *cobra.Command, opts runOptions) error {
960960
}
961961

962962
func RunServer(cmd *cobra.Command, _ []string) error {
963-
// retrieve the OLLAMA_HOST environment variable
964-
ollamaHost, err := api.GetOllamaHost()
965-
if err != nil {
966-
return err
967-
}
968-
969963
if err := initializeKeypair(); err != nil {
970964
return err
971965
}
972966

973-
ln, err := net.Listen("tcp", net.JoinHostPort(ollamaHost.Host, ollamaHost.Port))
967+
ln, err := net.Listen("tcp", net.JoinHostPort(envconfig.Host.Host, envconfig.Host.Port))
974968
if err != nil {
975969
return err
976970
}

envconfig/config.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package envconfig
22

33
import (
4+
"errors"
45
"fmt"
56
"log/slog"
67
"net"
@@ -11,6 +12,18 @@ import (
1112
"strings"
1213
)
1314

15+
type OllamaHost struct {
16+
Scheme string
17+
Host string
18+
Port string
19+
}
20+
21+
func (o OllamaHost) String() string {
22+
return fmt.Sprintf("%s://%s:%s", o.Scheme, o.Host, o.Port)
23+
}
24+
25+
var ErrInvalidHostPort = errors.New("invalid port specified in OLLAMA_HOST")
26+
1427
var (
1528
// Set via OLLAMA_ORIGINS in the environment
1629
AllowOrigins []string
@@ -34,6 +47,8 @@ var (
3447
NoPrune bool
3548
// Set via OLLAMA_NUM_PARALLEL in the environment
3649
NumParallel int
50+
// Set via OLLAMA_HOST in the environment
51+
Host *OllamaHost
3752
// Set via OLLAMA_RUNNERS_DIR in the environment
3853
RunnersDir string
3954
// Set via OLLAMA_TMPDIR in the environment
@@ -50,7 +65,7 @@ func AsMap() map[string]EnvVar {
5065
return map[string]EnvVar{
5166
"OLLAMA_DEBUG": {"OLLAMA_DEBUG", Debug, "Show additional debug information (e.g. OLLAMA_DEBUG=1)"},
5267
"OLLAMA_FLASH_ATTENTION": {"OLLAMA_FLASH_ATTENTION", FlashAttention, "Enabled flash attention"},
53-
"OLLAMA_HOST": {"OLLAMA_HOST", "", "IP Address for the ollama server (default 127.0.0.1:11434)"},
68+
"OLLAMA_HOST": {"OLLAMA_HOST", Host, "IP Address for the ollama server (default 127.0.0.1:11434)"},
5469
"OLLAMA_KEEP_ALIVE": {"OLLAMA_KEEP_ALIVE", KeepAlive, "The duration that models stay loaded in memory (default \"5m\")"},
5570
"OLLAMA_LLM_LIBRARY": {"OLLAMA_LLM_LIBRARY", LLMLibrary, "Set LLM library to bypass autodetection"},
5671
"OLLAMA_MAX_LOADED_MODELS": {"OLLAMA_MAX_LOADED_MODELS", MaxRunners, "Maximum number of loaded models (default 1)"},
@@ -216,4 +231,54 @@ func LoadConfig() {
216231
}
217232

218233
KeepAlive = clean("OLLAMA_KEEP_ALIVE")
234+
235+
var err error
236+
Host, err = getOllamaHost()
237+
if err != nil {
238+
slog.Error("invalid setting", "OLLAMA_HOST", Host, "error", err, "using default port", Host.Port)
239+
}
240+
}
241+
242+
func getOllamaHost() (*OllamaHost, error) {
243+
defaultPort := "11434"
244+
245+
hostVar := os.Getenv("OLLAMA_HOST")
246+
hostVar = strings.TrimSpace(strings.Trim(strings.TrimSpace(hostVar), "\"'"))
247+
248+
scheme, hostport, ok := strings.Cut(hostVar, "://")
249+
switch {
250+
case !ok:
251+
scheme, hostport = "http", hostVar
252+
case scheme == "http":
253+
defaultPort = "80"
254+
case scheme == "https":
255+
defaultPort = "443"
256+
}
257+
258+
// trim trailing slashes
259+
hostport = strings.TrimRight(hostport, "/")
260+
261+
host, port, err := net.SplitHostPort(hostport)
262+
if err != nil {
263+
host, port = "127.0.0.1", defaultPort
264+
if ip := net.ParseIP(strings.Trim(hostport, "[]")); ip != nil {
265+
host = ip.String()
266+
} else if hostport != "" {
267+
host = hostport
268+
}
269+
}
270+
271+
if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 0 {
272+
return &OllamaHost{
273+
Scheme: scheme,
274+
Host: host,
275+
Port: defaultPort,
276+
}, ErrInvalidHostPort
277+
}
278+
279+
return &OllamaHost{
280+
Scheme: scheme,
281+
Host: host,
282+
Port: port,
283+
}, nil
219284
}

envconfig/config_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package envconfig
22

33
import (
4+
"fmt"
5+
"net"
46
"testing"
57

8+
"github.com/stretchr/testify/assert"
69
"github.com/stretchr/testify/require"
710
)
811

@@ -21,3 +24,48 @@ func TestConfig(t *testing.T) {
2124
LoadConfig()
2225
require.True(t, FlashAttention)
2326
}
27+
28+
func TestClientFromEnvironment(t *testing.T) {
29+
type testCase struct {
30+
value string
31+
expect string
32+
err error
33+
}
34+
35+
hostTestCases := map[string]*testCase{
36+
"empty": {value: "", expect: "127.0.0.1:11434"},
37+
"only address": {value: "1.2.3.4", expect: "1.2.3.4:11434"},
38+
"only port": {value: ":1234", expect: ":1234"},
39+
"address and port": {value: "1.2.3.4:1234", expect: "1.2.3.4:1234"},
40+
"hostname": {value: "example.com", expect: "example.com:11434"},
41+
"hostname and port": {value: "example.com:1234", expect: "example.com:1234"},
42+
"zero port": {value: ":0", expect: ":0"},
43+
"too large port": {value: ":66000", err: ErrInvalidHostPort},
44+
"too small port": {value: ":-1", err: ErrInvalidHostPort},
45+
"ipv6 localhost": {value: "[::1]", expect: "[::1]:11434"},
46+
"ipv6 world open": {value: "[::]", expect: "[::]:11434"},
47+
"ipv6 no brackets": {value: "::1", expect: "[::1]:11434"},
48+
"ipv6 + port": {value: "[::1]:1337", expect: "[::1]:1337"},
49+
"extra space": {value: " 1.2.3.4 ", expect: "1.2.3.4:11434"},
50+
"extra quotes": {value: "\"1.2.3.4\"", expect: "1.2.3.4:11434"},
51+
"extra space+quotes": {value: " \" 1.2.3.4 \" ", expect: "1.2.3.4:11434"},
52+
"extra single quotes": {value: "'1.2.3.4'", expect: "1.2.3.4:11434"},
53+
}
54+
55+
for k, v := range hostTestCases {
56+
t.Run(k, func(t *testing.T) {
57+
t.Setenv("OLLAMA_HOST", v.value)
58+
LoadConfig()
59+
60+
oh, err := getOllamaHost()
61+
if err != v.err {
62+
t.Fatalf("expected %s, got %s", v.err, err)
63+
}
64+
65+
if err == nil {
66+
host := net.JoinHostPort(oh.Host, oh.Port)
67+
assert.Equal(t, v.expect, host, fmt.Sprintf("%s: expected %s, got %s", k, v.expect, host))
68+
}
69+
})
70+
}
71+
}

0 commit comments

Comments
 (0)