Kaynağa Gözat

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 hafta önce
ebeveyn
işleme
6e81faa698
2 değiştirilmiş dosya ile 35 ekleme ve 41 silme
  1. +19
    -12
      relay/channel/api_request.go
  2. +16
    -29
      relay/channel/api_request_test.go

+ 19
- 12
relay/channel/api_request.go Dosyayı Görüntüle

@@ -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)
}


+ 16
- 29
relay/channel/api_request_test.go Dosyayı Görüntüle

@@ -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)
}



Yükleniyor…
İptal
Kaydet