Repository: ollama/ollama
Pull request: cloud_proxy: send ollama client version

This was previously included in the user agent, and we've made use of it in the past to hotpatch bugs server-side for particular Ollama versions.

Changed files:
filename: server/cloud_proxy.go
patch: @@ -20,13 +20,15 @@ import (
 	"github.com/ollama/ollama/auth"
 	"github.com/ollama/ollama/envconfig"
 	internalcloud "github.com/ollama/ollama/internal/cloud"
+	"github.com/ollama/ollama/version"
 )
 
 const (
-	defaultCloudProxyBaseURL     = "https://ollama.com:443"
-	defaultCloudProxySigningHost = "ollama.com"
-	cloudProxyBaseURLEnv         = "OLLAMA_CLOUD_BASE_URL"
-	legacyCloudAnthropicKey      = "legacy_cloud_anthropic_web_search"
+	defaultCloudProxyBaseURL      = "https://ollama.com:443"
+	defaultCloudProxySigningHost  = "ollama.com"
+	cloudProxyBaseURLEnv          = "OLLAMA_CLOUD_BASE_URL"
+	legacyCloudAnthropicKey       = "legacy_cloud_anthropic_web_search"
+	cloudProxyClientVersionHeader = "X-Ollama-Client-Version"
 )
 
 var (
@@ -181,6 +183,9 @@ func proxyCloudRequestWithPath(c *gin.Context, body []byte, path string, disable
 	}
 
 	copyProxyRequestHeaders(outReq.Header, c.Request.Header)
+	if clientVersion := strings.TrimSpace(version.Version); clientVersion != "" {
+		outReq.Header.Set(cloudProxyClientVersionHeader, clientVersion)
+	}
 	if outReq.Header.Get("Content-Type") == "" && len(body) > 0 {
 		outReq.Header.Set("Content-Type", "application/json")
 	}

filename: server/routes_cloud_test.go
patch: @@ -17,6 +17,7 @@ import (
 	"github.com/ollama/ollama/api"
 	internalcloud "github.com/ollama/ollama/internal/cloud"
 	"github.com/ollama/ollama/middleware"
+	"github.com/ollama/ollama/version"
 )
 
 func TestStatusHandler(t *testing.T) {
@@ -207,6 +208,9 @@ func TestExplicitCloudPassthroughAPIAndV1(t *testing.T) {
 		if got := capture.header.Get("X-Test-Header"); got != "api-header" {
 			t.Fatalf("expected forwarded X-Test-Header=api-header, got %q", got)
 		}
+		if got := capture.header.Get(cloudProxyClientVersionHeader); got != version.Version {
+			t.Fatalf("expected %s=%q, got %q", cloudProxyClientVersionHeader, version.Version, got)
+		}
 	})
 
 	t.Run("api chat", func(t *testing.T) {

filename: server/routes_web_experimental_test.go
patch: @@ -12,6 +12,7 @@ import (
 
 	"github.com/gin-gonic/gin"
 	internalcloud "github.com/ollama/ollama/internal/cloud"
+	"github.com/ollama/ollama/version"
 )
 
 type webExperimentalUpstreamCapture struct {
@@ -92,6 +93,7 @@ func TestExperimentalWebEndpointsPassthrough(t *testing.T) {
 			req.Header.Set("Content-Type", "application/json")
 			req.Header.Set("Authorization", "Bearer should-forward")
 			req.Header.Set("X-Test-Header", "web-experimental")
+			req.Header.Set(cloudProxyClientVersionHeader, "should-be-overwritten")
 
 			resp, err := local.Client().Do(req)
 			if err != nil {
@@ -115,6 +117,9 @@ func TestExperimentalWebEndpointsPassthrough(t *testing.T) {
 			if got := capture.header.Get("X-Test-Header"); got != "web-experimental" {
 				t.Fatalf("expected forwarded X-Test-Header=web-experimental, got %q", got)
 			}
+			if got := capture.header.Get(cloudProxyClientVersionHeader); got != version.Version {
+				t.Fatalf("expected %s=%q, got %q", cloudProxyClientVersionHeader, version.Version, got)
+			}
 		})
 	}
 }