Merge pull request #1895 from gaoren002/fix/codex-spark-limitations

fix(openai): handle codex spark model limitations
This commit is contained in:
Wesley Liddick
2026-04-24 19:57:42 +08:00
committed by GitHub
5 changed files with 257 additions and 2 deletions

View File

@@ -48,6 +48,8 @@ type codexTransformResult struct {
const (
codexImageGenerationBridgeMarker = "<sub2api-codex-image-generation>"
codexImageGenerationBridgeText = codexImageGenerationBridgeMarker + "\nWhen the user asks for raster image generation or editing, use the OpenAI Responses native `image_generation` tool attached to this request. The local Codex client may not expose an `image_gen` namespace, but that does not mean image generation is unavailable. Do not ask the user to switch to CLI fallback solely because `image_gen` is absent.\n</sub2api-codex-image-generation>"
codexSparkImageUnsupportedMarker = "<sub2api-codex-spark-image-unsupported>"
codexSparkImageUnsupportedText = codexSparkImageUnsupportedMarker + "\nThe current model is gpt-5.3-codex-spark, which does not support image generation, image editing, image input, the `image_generation` tool, or Codex `image_gen`/`$imagegen` workflows. If the user asks for image generation or image editing, clearly explain this model limitation and ask them to switch to a non-Spark Codex model such as gpt-5.3-codex or gpt-5.4. Do not claim that the local environment merely lacks image_gen tooling, and do not suggest CLI fallback as the primary fix while the model remains Spark.\n</sub2api-codex-spark-image-unsupported>"
)
func applyCodexOAuthTransform(reqBody map[string]any, isCodexCLI bool, isCompact bool) codexTransformResult {
@@ -165,6 +167,9 @@ func applyCodexOAuthTransform(reqBody map[string]any, isCodexCLI bool, isCompact
if applyInstructions(reqBody, isCodexCLI) {
result.Modified = true
}
if isCodexSparkModel(normalizedModel) && applyCodexSparkImageUnsupportedInstructions(reqBody) {
result.Modified = true
}
// 续链场景保留 item_reference 与 id避免 call_id 上下文丢失。
if input, ok := reqBody["input"].([]any); ok {
@@ -244,6 +249,10 @@ func normalizeCodexModel(model string) string {
return "gpt-5.4"
}
func isCodexSparkModel(model string) bool {
return normalizeCodexModel(model) == "gpt-5.3-codex-spark"
}
func hasOpenAIImageGenerationTool(reqBody map[string]any) bool {
rawTools, ok := reqBody["tools"]
if !ok || rawTools == nil {
@@ -265,6 +274,40 @@ func hasOpenAIImageGenerationTool(reqBody map[string]any) bool {
return false
}
func hasOpenAIInputImage(reqBody map[string]any) bool {
if reqBody == nil {
return false
}
return hasOpenAIInputImageValue(reqBody["input"]) || hasOpenAIInputImageValue(reqBody["messages"])
}
func hasOpenAIInputImageValue(value any) bool {
switch v := value.(type) {
case []any:
for _, item := range v {
if hasOpenAIInputImageValue(item) {
return true
}
}
case map[string]any:
if strings.TrimSpace(firstNonEmptyString(v["type"])) == "input_image" {
return true
}
if _, ok := v["image_url"]; ok {
return true
}
return hasOpenAIInputImageValue(v["content"])
}
return false
}
func validateCodexSparkInput(reqBody map[string]any, model string) error {
if !isCodexSparkModel(model) || !hasOpenAIInputImage(reqBody) {
return nil
}
return fmt.Errorf("model %q does not support image input", strings.TrimSpace(model))
}
func normalizeOpenAIResponsesImageGenerationTools(reqBody map[string]any) bool {
rawTools, ok := reqBody["tools"]
if !ok || rawTools == nil {
@@ -309,6 +352,9 @@ func ensureOpenAIResponsesImageGenerationTool(reqBody map[string]any) bool {
if len(reqBody) == 0 {
return false
}
if isCodexSparkModel(firstNonEmptyString(reqBody["model"])) {
return false
}
tool := map[string]any{
"type": "image_generation",
@@ -344,6 +390,9 @@ func applyCodexImageGenerationBridgeInstructions(reqBody map[string]any) bool {
if len(reqBody) == 0 || !hasOpenAIImageGenerationTool(reqBody) {
return false
}
if isCodexSparkModel(firstNonEmptyString(reqBody["model"])) {
return false
}
existing, _ := reqBody["instructions"].(string)
if strings.Contains(existing, codexImageGenerationBridgeMarker) {
@@ -360,6 +409,23 @@ func applyCodexImageGenerationBridgeInstructions(reqBody map[string]any) bool {
return true
}
func applyCodexSparkImageUnsupportedInstructions(reqBody map[string]any) bool {
if len(reqBody) == 0 {
return false
}
existing, _ := reqBody["instructions"].(string)
if strings.Contains(existing, codexSparkImageUnsupportedMarker) {
return false
}
existing = strings.TrimRight(existing, " \t\r\n")
if strings.TrimSpace(existing) == "" {
reqBody["instructions"] = codexSparkImageUnsupportedText
return true
}
reqBody["instructions"] = existing + "\n\n" + codexSparkImageUnsupportedText
return true
}
func validateOpenAIResponsesImageModel(reqBody map[string]any, model string) error {
if !hasOpenAIImageGenerationTool(reqBody) {
return nil