diff --git a/relay/channel/api_request.go b/relay/channel/api_request.go index cbed0bd..26fcab6 100644 --- a/relay/channel/api_request.go +++ b/relay/channel/api_request.go @@ -279,17 +279,24 @@ func applyHeaderOverrideToRequest(req *http.Request, headerOverride map[string]s } } -func applyPassthroughModelMapping(body []byte, info *common.RelayInfo) []byte { - if len(body) == 0 || info == nil || info.ChannelMeta == nil { - return body +func needsPassthroughModelMapping(info *common.RelayInfo) bool { + if info == nil || info.ChannelMeta == nil { + return false } if !model_setting.GetGlobalSettings().PassthroughApplyModelMapping { - return body + return false } if !model_setting.GetGlobalSettings().PassThroughRequestEnabled && !info.ChannelMeta.ChannelSetting.PassThroughBodyEnabled { - return body + return false } if !info.ChannelMeta.IsModelMapped || strings.TrimSpace(info.ChannelMeta.UpstreamModelName) == "" { + return false + } + return true +} + +func applyPassthroughModelMapping(body []byte, info *common.RelayInfo) []byte { + if len(body) == 0 { return body } if common2.GetJsonType(body) != "object" { @@ -310,15 +317,15 @@ func DoApiRequest(a Adaptor, c *gin.Context, info *common.RelayInfo, requestBody if common2.DebugEnabled { common2.SysLog("fullRequestURL: " + fullRequestURL) } - var bodyBytes []byte - if requestBody != nil { - bodyBytes, err = io.ReadAll(requestBody) - if err != nil { - return nil, fmt.Errorf("read request body failed: %w", err) + var bodyReader io.Reader = requestBody + if needsPassthroughModelMapping(info) && requestBody != nil { + bodyBytes, readErr := io.ReadAll(requestBody) + if readErr != nil { + return nil, fmt.Errorf("read request body failed: %w", readErr) } + bodyReader = bytes.NewReader(applyPassthroughModelMapping(bodyBytes, info)) } - bodyBytes = applyPassthroughModelMapping(bodyBytes, info) - req, err := http.NewRequest(c.Request.Method, fullRequestURL, bytes.NewReader(bodyBytes)) + req, err := http.NewRequest(c.Request.Method, fullRequestURL, bodyReader) if err != nil { return nil, fmt.Errorf("new request failed: %w", err) } diff --git a/relay/channel/api_request_test.go b/relay/channel/api_request_test.go index ede3f41..f8c5db8 100644 --- a/relay/channel/api_request_test.go +++ b/relay/channel/api_request_test.go @@ -12,10 +12,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestApplyPassthroughModelMapping(t *testing.T) { - body := []byte(`{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}`) - info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}} - +func TestPassthroughModelMapping_AppliedWhenEnabled(t *testing.T) { origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping defer func() { @@ -26,14 +23,15 @@ func TestApplyPassthroughModelMapping(t *testing.T) { model_setting.GetGlobalSettings().PassThroughRequestEnabled = true model_setting.GetGlobalSettings().PassthroughApplyModelMapping = true + info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}} + body := []byte(`{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}`) + + assert.True(t, needsPassthroughModelMapping(info)) got := applyPassthroughModelMapping(body, info) assert.JSONEq(t, `{"model":"gpt-4.1","messages":[{"role":"user","content":"hi"}]}`, string(got)) } -func TestApplyPassthroughModelMapping_NonJSONKeepsOriginal(t *testing.T) { - body := []byte(`model=gpt-4o`) - info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}} - +func TestPassthroughModelMapping_SkippedWhenNotMapped(t *testing.T) { origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping defer func() { @@ -44,14 +42,11 @@ func TestApplyPassthroughModelMapping_NonJSONKeepsOriginal(t *testing.T) { model_setting.GetGlobalSettings().PassThroughRequestEnabled = true model_setting.GetGlobalSettings().PassthroughApplyModelMapping = true - got := applyPassthroughModelMapping(body, info) - assert.Equal(t, body, got) -} - -func TestApplyPassthroughModelMapping_DisabledKeepsOriginal(t *testing.T) { - body := []byte(`{"model":"gpt-4o"}`) info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: false, UpstreamModelName: "gpt-4.1"}} + assert.False(t, needsPassthroughModelMapping(info)) +} +func TestPassthroughModelMapping_SkippedWhenPassthroughOff(t *testing.T) { origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping defer func() { @@ -59,26 +54,18 @@ func TestApplyPassthroughModelMapping_DisabledKeepsOriginal(t *testing.T) { model_setting.GetGlobalSettings().PassthroughApplyModelMapping = origApplyMapping }() - model_setting.GetGlobalSettings().PassThroughRequestEnabled = true + model_setting.GetGlobalSettings().PassThroughRequestEnabled = false model_setting.GetGlobalSettings().PassthroughApplyModelMapping = true - got := applyPassthroughModelMapping(body, info) - assert.Equal(t, body, got) + info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}} + assert.False(t, needsPassthroughModelMapping(info)) } -func TestDoApiRequest_DoesNotChangeNonPassthroughBody(t *testing.T) { - origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled - origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping - defer func() { - model_setting.GetGlobalSettings().PassThroughRequestEnabled = origRequestEnabled - model_setting.GetGlobalSettings().PassthroughApplyModelMapping = origApplyMapping - }() - - model_setting.GetGlobalSettings().PassThroughRequestEnabled = false - model_setting.GetGlobalSettings().PassthroughApplyModelMapping = true +func TestPassthroughModelMapping_NonJSONKeepsOriginal(t *testing.T) { + body := []byte(`model=gpt-4o`) + info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}} - body := []byte(`{"model":"gpt-4o"}`) - got := applyPassthroughModelMapping(body, &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{IsModelMapped: true, UpstreamModelName: "gpt-4.1"}}) + got := applyPassthroughModelMapping(body, info) assert.Equal(t, body, got) }