Skip to content

Commit e6889f7

Browse files
committed
fix: skip default image_generation tool injection for gpt-5.3-codex-spark
gpt-5.3-codex-spark is a ChatGPT-account text-only Codex model; the upstream rejects requests carrying the hosted image_generation tool. PrepareResponsesBody used to auto-inject the default image tool (and its bridge instructions) for any text request, so spark requests failed on the HTTP upstream path where the WS-only stripping from issue #220 never runs. Guard the auto-injection by model while still honoring explicitly supplied image tools. Closes #230
1 parent 6616b62 commit e6889f7

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

proxy/translator.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,23 @@ func hasStructuredResponsesFormat(body map[string]any) bool {
624624
return false
625625
}
626626

627+
// responsesModelRejectsHostedImageTool 判断模型是否不支持 hosted image_generation
628+
// 工具。gpt-5.3-codex-spark 是 ChatGPT 账号下的纯文本 Codex 模型,上游会直接拒绝
629+
// 带 hosted 图片工具的请求(issue #230)。
630+
func responsesModelRejectsHostedImageTool(body map[string]any) bool {
631+
model := strings.TrimSpace(firstNonEmptyAnyString(body["model"]))
632+
return strings.EqualFold(model, proOnlySparkModel)
633+
}
634+
627635
func shouldAutoInjectResponsesImageGenerationTool(body map[string]any) bool {
628636
if len(body) == 0 || hasResponsesImageGenerationTool(body) {
629637
return false
630638
}
639+
// 不为拒绝 hosted 图片工具的模型自动注入默认图片工具及桥接 instructions;
640+
// 用户显式自带的图片工具仍由上面 hasResponsesImageGenerationTool 分支保留。
641+
if responsesModelRejectsHostedImageTool(body) {
642+
return false
643+
}
631644
if hasResponsesImageGenerationToolChoice(body) {
632645
return true
633646
}

proxy/translator_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,33 @@ func TestPrepareResponsesBody_ToolChoiceImageGenerationAutoInjectsTool(t *testin
944944
}
945945
}
946946

947+
func TestPrepareResponsesBody_SparkModelSkipsDefaultImageGenerationTool(t *testing.T) {
948+
raw := []byte(`{"model":"gpt-5.3-codex-spark","input":"hi"}`)
949+
950+
got, _ := PrepareResponsesBody(raw)
951+
952+
if gjson.GetBytes(got, "tools").Exists() {
953+
t.Fatalf("did not expect default image_generation tool for spark model, got %s", string(got))
954+
}
955+
if instructions := gjson.GetBytes(got, "instructions").String(); strings.Contains(instructions, codexImageGenerationBridgeMarker) {
956+
t.Fatalf("did not expect image generation bridge instructions for spark model, got %q", instructions)
957+
}
958+
}
959+
960+
func TestPrepareResponsesBody_SparkModelKeepsExplicitImageGenerationTool(t *testing.T) {
961+
raw := []byte(`{
962+
"model":"gpt-5.3-codex-spark",
963+
"input":"draw a poster",
964+
"tools":[{"type":"image_generation"}]
965+
}`)
966+
967+
got, _ := PrepareResponsesBody(raw)
968+
969+
if toolType := gjson.GetBytes(got, "tools.0.type").String(); toolType != "image_generation" {
970+
t.Fatalf("explicit image_generation tool should be kept for spark model, got %s", string(got))
971+
}
972+
}
973+
947974
func TestPrepareResponsesBody_NormalizesNestedReasoningEffortAliases(t *testing.T) {
948975
raw := []byte(`{
949976
"model":"gpt-5.4",

0 commit comments

Comments
 (0)