소스 검색

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 주 전
부모
커밋
6e81faa698
2개의 변경된 파일35개의 추가작업 그리고 41개의 파일을 삭제
  1. +19
    -12
      relay/channel/api_request.go
  2. +16
    -29
      relay/channel/api_request_test.go

+ 19
- 12
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)
}


+ 16
- 29
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)
}



불러오는 중...
취소
저장