fix(openai): 避免 pool 两跳重复计算 system 提示词

This commit is contained in:
feeeei
2026-08-26 19:50:11 +08:00
parent efb46db0a9
commit d881bfc0de
2 changed files with 56 additions and 2 deletions
@@ -472,13 +472,29 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
if decodeErr != nil {
return nil, decodeErr
}
// Responses OAuth 与 Chat 兼容入口保持一致:纯文本 system 可以无损提升后删除,
// JSON object 模式仍需在 input 中保留 JSON 指令供上游兼容校验。
omitPromotedSystemMessages := !strings.EqualFold(
strings.TrimSpace(gjson.GetBytes(body, "text.format.type").String()),
"json_object",
)
codexResult := codexTransformResult{}
if compatMessagesBridge {
codexResult = applyCodexOAuthTransformWithOptions(decoded, codexOAuthTransformOptions{IsCodexCLI: isCodexCLI, IsCompact: isCompactRequest, SkipDefaultInstructions: true, PreserveToolCallIDs: true})
codexResult = applyCodexOAuthTransformWithOptions(decoded, codexOAuthTransformOptions{
IsCodexCLI: isCodexCLI,
IsCompact: isCompactRequest,
SkipDefaultInstructions: true,
PreserveToolCallIDs: true,
OmitPromotedSystemMessagesFromInput: omitPromotedSystemMessages,
})
ensureCodexOAuthInstructionsField(decoded)
markDecodedModified()
} else {
codexResult = applyCodexOAuthTransform(decoded, isCodexCLI, isCompactRequest)
codexResult = applyCodexOAuthTransformWithOptions(decoded, codexOAuthTransformOptions{
IsCodexCLI: isCodexCLI,
IsCompact: isCompactRequest,
OmitPromotedSystemMessagesFromInput: omitPromotedSystemMessages,
})
}
if codexResult.Error != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": gin.H{"type": "invalid_request_error", "message": codexResult.Error.Error()}})
@@ -132,6 +132,44 @@ func TestOpenAIGatewayService_ResponsesUnknownModelDoesNotFallbackToGPT54(t *tes
require.True(t, rec.Code >= http.StatusBadRequest)
}
func TestOpenAIGatewayService_OAuthResponsesPromotesSystemMessageWithoutDuplication(t *testing.T) {
gin.SetMode(gin.TestMode)
const systemPrompt = "Unique system prefix for Responses token accounting."
const existingInstructions = "Existing instructions."
body := []byte(`{"model":"gpt-5.4","stream":false,"instructions":"` + existingInstructions + `","input":[{"role":"system","content":"` + systemPrompt + `"},{"role":"user","content":"hello"}]}`)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", bytes.NewReader(body))
c.Request.Header.Set("Content-Type", "application/json")
upstream := &httpUpstreamRecorder{err: errors.New("stop after capture")}
svc := &OpenAIGatewayService{cfg: &config.Config{}, httpUpstream: upstream}
account := &Account{
ID: 124,
Name: "openai-oauth",
Platform: PlatformOpenAI,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "oauth-token",
"chatgpt_account_id": "chatgpt-acc",
},
Status: StatusActive,
Schedulable: true,
}
result, err := svc.Forward(context.Background(), c, account, body)
require.Error(t, err)
require.Nil(t, result)
require.NotEmpty(t, upstream.lastBody)
require.Equal(t, systemPrompt+"\n\n"+existingInstructions, gjson.GetBytes(upstream.lastBody, "instructions").String())
require.Equal(t, int64(1), gjson.GetBytes(upstream.lastBody, "input.#").Int())
require.Equal(t, "user", gjson.GetBytes(upstream.lastBody, "input.0.role").String())
require.Equal(t, 1, strings.Count(string(upstream.lastBody), systemPrompt))
}
func TestOpenAIGatewayService_NativeResponsesBodyModificationPreservesHTMLChars(t *testing.T) {
gin.SetMode(gin.TestMode)