diff --git a/object/object.go b/object/object.go index c62b1cbec4735a031c652f26a811bc6053dc7ff1..16c77d1cf3f62807672f30f605038133bc5bd143 100644 --- a/object/object.go +++ b/object/object.go @@ -120,6 +120,7 @@ func GenerateWithTool( TopK: call.TopK, PresencePenalty: call.PresencePenalty, FrequencyPenalty: call.FrequencyPenalty, + UserAgent: call.UserAgent, ProviderOptions: call.ProviderOptions, }) if err != nil { @@ -212,6 +213,7 @@ func GenerateWithText( TopK: call.TopK, PresencePenalty: call.PresencePenalty, FrequencyPenalty: call.FrequencyPenalty, + UserAgent: call.UserAgent, ProviderOptions: call.ProviderOptions, }) if err != nil { @@ -294,6 +296,7 @@ func StreamWithTool( TopK: call.TopK, PresencePenalty: call.PresencePenalty, FrequencyPenalty: call.FrequencyPenalty, + UserAgent: call.UserAgent, ProviderOptions: call.ProviderOptions, }) if err != nil { @@ -503,6 +506,7 @@ func StreamWithText( TopK: call.TopK, PresencePenalty: call.PresencePenalty, FrequencyPenalty: call.FrequencyPenalty, + UserAgent: call.UserAgent, ProviderOptions: call.ProviderOptions, }) if err != nil { diff --git a/providers/internal/httpheaders/httpheaders.go b/providers/internal/httpheaders/httpheaders.go index 0dcd54799d11cc63b7ec6624be4565dcb0c94705..0bda5f5697d27d540d1bc42074c38a90a13d0992 100644 --- a/providers/internal/httpheaders/httpheaders.go +++ b/providers/internal/httpheaders/httpheaders.go @@ -34,7 +34,11 @@ func ResolveHeaders(headers map[string]string, explicitUA, defaultUA string) map } out["User-Agent"] = explicitUA case len(uaKeys) > 0: - // keep the header-map value as-is + val := out[uaKeys[0]] + for _, k := range uaKeys { + delete(out, k) + } + out["User-Agent"] = val default: out["User-Agent"] = defaultUA } diff --git a/providers/internal/httpheaders/httpheaders_test.go b/providers/internal/httpheaders/httpheaders_test.go index 3a70293a77231e39945d060c14e47af61667cabc..d40158ab04aab32263ed37c9205f6597a61ca7c2 100644 --- a/providers/internal/httpheaders/httpheaders_test.go +++ b/providers/internal/httpheaders/httpheaders_test.go @@ -60,11 +60,13 @@ func TestResolveHeaders_Precedence(t *testing.T) { assert.False(t, hasLower, "old case-insensitive key should be removed") }) - t.Run("case-insensitive header key preserved when no explicit UA", func(t *testing.T) { + t.Run("case-insensitive header key canonicalized when no explicit UA", func(t *testing.T) { t.Parallel() headers := map[string]string{"user-agent": "from-headers"} got := ResolveHeaders(headers, "", "default-ua") - assert.Equal(t, "from-headers", got["user-agent"]) + assert.Equal(t, "from-headers", got["User-Agent"]) + _, hasLower := got["user-agent"] + assert.False(t, hasLower, "non-canonical key should be removed") }) } @@ -95,14 +97,30 @@ func TestResolveHeaders_PreservesOtherHeaders(t *testing.T) { func TestResolveHeaders_DuplicateCaseInsensitiveKeys(t *testing.T) { t.Parallel() - headers := map[string]string{ - "User-Agent": "canonical", - "user-agent": "lowercase", - } - got := ResolveHeaders(headers, "explicit", "default") - assert.Equal(t, "explicit", got["User-Agent"]) - _, hasLower := got["user-agent"] - assert.False(t, hasLower, "all case-insensitive UA keys must be removed") + t.Run("explicit UA removes all variants", func(t *testing.T) { + t.Parallel() + headers := map[string]string{ + "User-Agent": "canonical", + "user-agent": "lowercase", + } + got := ResolveHeaders(headers, "explicit", "default") + assert.Equal(t, "explicit", got["User-Agent"]) + _, hasLower := got["user-agent"] + assert.False(t, hasLower, "all case-insensitive UA keys must be removed") + }) + + t.Run("no explicit UA collapses to single canonical key", func(t *testing.T) { + t.Parallel() + headers := map[string]string{ + "User-Agent": "canonical", + "user-agent": "lowercase", + } + got := ResolveHeaders(headers, "", "default") + _, hasLower := got["user-agent"] + hasCanonical := got["User-Agent"] + assert.False(t, hasLower, "non-canonical key should be removed") + assert.NotEmpty(t, hasCanonical, "canonical User-Agent key must exist") + }) } func TestCallUserAgent(t *testing.T) {