From b7d88f0b0904e427b5e31cdf0dabbae0f8cd7279 Mon Sep 17 00:00:00 2001 From: glorydavid03023 Date: Thu, 28 May 2026 03:46:58 -0500 Subject: [PATCH] fix(go-models): harden Voyage default transport handling (#15341) ## Summary - Harden `NewVoyageModel` to avoid panics when `http.DefaultTransport` is a custom non-`*http.Transport` RoundTripper. - Fallback to a safe transport (`ProxyFromEnvironment`) while preserving existing pooling/timeout settings. - Add `TestVoyageNewModelWithCustomDefaultTransport` regression coverage. Co-authored-by: Cursor --- internal/entity/models/voyage.go | 10 +++++++++- internal/entity/models/voyage_test.go | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/internal/entity/models/voyage.go b/internal/entity/models/voyage.go index c17a0e8081..c51d8d9269 100644 --- a/internal/entity/models/voyage.go +++ b/internal/entity/models/voyage.go @@ -56,7 +56,15 @@ type VoyageModel struct { // TLSHandshakeTimeout, and ExpectContinueTimeout, and only override // the connection-pool fields we care about. func NewVoyageModel(baseURL map[string]string, urlSuffix URLSuffix) *VoyageModel { - transport := http.DefaultTransport.(*http.Transport).Clone() + defaultTransport, ok := http.DefaultTransport.(*http.Transport) + var transport *http.Transport + if ok { + transport = defaultTransport.Clone() + } else { + transport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + } + } transport.MaxIdleConns = 100 transport.MaxIdleConnsPerHost = 10 transport.IdleConnTimeout = 90 * time.Second diff --git a/internal/entity/models/voyage_test.go b/internal/entity/models/voyage_test.go index 255915bf98..1eb6dbc0d7 100644 --- a/internal/entity/models/voyage_test.go +++ b/internal/entity/models/voyage_test.go @@ -9,6 +9,12 @@ import ( "testing" ) +type roundTripperFunc func(*http.Request) (*http.Response, error) + +func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { + return f(r) +} + func newVoyageServer(t *testing.T, expectedPath string, handler func(t *testing.T, body map[string]interface{}, w http.ResponseWriter)) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -51,6 +57,20 @@ func TestVoyageName(t *testing.T) { } } +func TestVoyageNewModelWithCustomDefaultTransport(t *testing.T) { + original := http.DefaultTransport + http.DefaultTransport = roundTripperFunc(func(*http.Request) (*http.Response, error) { + return nil, nil + }) + t.Cleanup(func() { + http.DefaultTransport = original + }) + + if model := NewVoyageModel(map[string]string{"default": "http://unused"}, URLSuffix{}); model == nil { + t.Fatal("NewVoyageModel returned nil") + } +} + func TestVoyageEmbedHappyPath(t *testing.T) { srv := newVoyageServer(t, "/v1/embeddings", func(t *testing.T, body map[string]interface{}, w http.ResponseWriter) { if body["model"] != "voyage-3.5" {