Browse Source

perf: lazy-read body in DoApiRequest for passthrough model mapping

DoApiRequest previously called io.ReadAll on every request body to apply
passthrough model mapping, even when the feature was disabled. This
caused unnecessary memory pressure for large request bodies (long
conversations, multipart image uploads).

Split the guard logic into needsPassthroughModelMapping() so that
ReadAll is only called when all conditions are met (feature enabled +
passthrough mode + model mapped). Non-passthrough requests now pass
the original io.Reader directly to http.NewRequest without buffering.

Rewrite tests to verify combined guard + modify behavior.
master
fengsilin 2 weeks ago
parent
commit
6e81faa698
2 changed files with 35 additions and 41 deletions
  1. +19
    -12
      relay/channel/api_request.go
  2. +16
    -29
      relay/channel/api_request_test.go

+ 19
- 12
relay/channel/api_request.go View File

@@ -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 { if !model_setting.GetGlobalSettings().PassthroughApplyModelMapping {
return body
return false
} }
if !model_setting.GetGlobalSettings().PassThroughRequestEnabled && !info.ChannelMeta.ChannelSetting.PassThroughBodyEnabled { if !model_setting.GetGlobalSettings().PassThroughRequestEnabled && !info.ChannelMeta.ChannelSetting.PassThroughBodyEnabled {
return body
return false
} }
if !info.ChannelMeta.IsModelMapped || strings.TrimSpace(info.ChannelMeta.UpstreamModelName) == "" { 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 return body
} }
if common2.GetJsonType(body) != "object" { if common2.GetJsonType(body) != "object" {
@@ -310,15 +317,15 @@ func DoApiRequest(a Adaptor, c *gin.Context, info *common.RelayInfo, requestBody
if common2.DebugEnabled { if common2.DebugEnabled {
common2.SysLog("fullRequestURL: " + fullRequestURL) 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 { if err != nil {
return nil, fmt.Errorf("new request failed: %w", err) return nil, fmt.Errorf("new request failed: %w", err)
} }


+ 16
- 29
relay/channel/api_request_test.go View File

@@ -12,10 +12,7 @@ import (
"github.com/stretchr/testify/require" "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 origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled
origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping
defer func() { defer func() {
@@ -26,14 +23,15 @@ func TestApplyPassthroughModelMapping(t *testing.T) {
model_setting.GetGlobalSettings().PassThroughRequestEnabled = true model_setting.GetGlobalSettings().PassThroughRequestEnabled = true
model_setting.GetGlobalSettings().PassthroughApplyModelMapping = 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) got := applyPassthroughModelMapping(body, info)
assert.JSONEq(t, `{"model":"gpt-4.1","messages":[{"role":"user","content":"hi"}]}`, string(got)) 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 origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled
origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping
defer func() { defer func() {
@@ -44,14 +42,11 @@ func TestApplyPassthroughModelMapping_NonJSONKeepsOriginal(t *testing.T) {
model_setting.GetGlobalSettings().PassThroughRequestEnabled = true model_setting.GetGlobalSettings().PassThroughRequestEnabled = true
model_setting.GetGlobalSettings().PassthroughApplyModelMapping = 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"}} 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 origRequestEnabled := model_setting.GetGlobalSettings().PassThroughRequestEnabled
origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping origApplyMapping := model_setting.GetGlobalSettings().PassthroughApplyModelMapping
defer func() { defer func() {
@@ -59,26 +54,18 @@ func TestApplyPassthroughModelMapping_DisabledKeepsOriginal(t *testing.T) {
model_setting.GetGlobalSettings().PassthroughApplyModelMapping = origApplyMapping model_setting.GetGlobalSettings().PassthroughApplyModelMapping = origApplyMapping
}() }()


model_setting.GetGlobalSettings().PassThroughRequestEnabled = true
model_setting.GetGlobalSettings().PassThroughRequestEnabled = false
model_setting.GetGlobalSettings().PassthroughApplyModelMapping = true 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) assert.Equal(t, body, got)
} }




Loading…
Cancel
Save