fix(openai): skip Codex image bridge injections for /responses/compact

The Codex image_generation bridge block in Forward injects
tools:[image_generation] and tool_choice:"auto" (since #3498) into every
Codex request, including /responses/compact. The upstream compact endpoint
rejects tool_choice with 400 "Unknown parameter: 'tool_choice'", so every
remote compaction fails once the bridge is enabled. Guard the whole image
generation block with !isCompactRequest.

Fixes #3629
This commit is contained in:
seng1e
2026-07-02 11:09:50 +08:00
parent 7dc7cfce1d
commit c797159bf5
2 changed files with 32 additions and 1 deletions
@@ -2794,7 +2794,9 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
return nil, errors.New("image generation disabled for group")
}
if imageGenerationAllowed && (codexImageGenerationBridgeEnabled || isOpenAIImageGenerationModel(requestView.Model) || openAIRequestBodyImageGenerationToolNeedsNormalization(body) || isOpenAIImageGenerationModel(upstreamModel)) {
// /responses/compact 是会话压缩请求:上游不接受 tool_choice(400 unknown_parameter),
// 注入 image_generation 工具也没有意义,整块豁免。
if imageGenerationAllowed && !isCompactRequest && (codexImageGenerationBridgeEnabled || isOpenAIImageGenerationModel(requestView.Model) || openAIRequestBodyImageGenerationToolNeedsNormalization(body) || isOpenAIImageGenerationModel(upstreamModel)) {
decoded, decodeErr := ensureReqBody()
if decodeErr != nil {
return nil, decodeErr
@@ -208,6 +208,35 @@ func TestOpenAIGatewayServiceForward_CodexBridgePreservesExistingToolChoice(t *t
require.Equal(t, "image_generation", gjson.GetBytes(upstream.lastBody, "tool_choice.type").String())
}
func TestOpenAIGatewayServiceForward_CodexBridgeSkipsCompactRequests(t *testing.T) {
gin.SetMode(gin.TestMode)
upstream := &httpUpstreamRecorder{
resp: &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(strings.NewReader(`{"id":"resp_codex_compact","model":"gpt-5.4","usage":{"input_tokens":1,"output_tokens":1}}`)),
},
}
svc := newOpenAIImageGenerationControlTestService(upstream)
svc.cfg.Gateway.CodexImageGenerationBridgeEnabled = true
c, _ := newOpenAIImageGenerationControlTestContext(true, "codex_cli_rs/0.98.0")
c.Request = httptest.NewRequest(http.MethodPost, "/openai/v1/responses/compact", nil)
c.Request.Header.Set("User-Agent", "codex_cli_rs/0.98.0")
account := newOpenAIImageGenerationControlTestAccount()
// /responses/compact 上游不接受 tool_choice,bridge 注入必须整体豁免 compact 请求。
result, err := svc.Forward(context.Background(), c, account, []byte(`{"model":"gpt-5.4","input":"summarize the conversation","stream":false}`))
require.NoError(t, err)
require.NotNil(t, result)
require.NotNil(t, upstream.lastReq)
require.False(t, gjson.GetBytes(upstream.lastBody, "tool_choice").Exists())
require.False(t, gjson.GetBytes(upstream.lastBody, `tools.#(type=="image_generation")`).Exists())
instructions := gjson.GetBytes(upstream.lastBody, "instructions").String()
require.NotContains(t, instructions, "image_generation")
}
func TestOpenAIGatewayService_CodexImageGenerationBridgeOverridePrecedence(t *testing.T) {
groupID := int64(4242)