mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-03-24 20:57:39 +00:00
Merge pull request #2223 from cnrpman/fix/codex-responses-web-search-preview-compat
fix: normalize web_search_preview for codex responses
This commit is contained in:
@@ -3,6 +3,7 @@ package responses
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
@@ -39,6 +40,7 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte,
|
||||
|
||||
// Convert role "system" to "developer" in input array to comply with Codex API requirements.
|
||||
rawJSON = convertSystemRoleToDeveloper(rawJSON)
|
||||
rawJSON = normalizeCodexBuiltinTools(rawJSON)
|
||||
|
||||
return rawJSON
|
||||
}
|
||||
@@ -82,3 +84,59 @@ func convertSystemRoleToDeveloper(rawJSON []byte) []byte {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// normalizeCodexBuiltinTools rewrites legacy/preview built-in tool variants to the
|
||||
// stable names expected by the current Codex upstream.
|
||||
func normalizeCodexBuiltinTools(rawJSON []byte) []byte {
|
||||
result := rawJSON
|
||||
|
||||
tools := gjson.GetBytes(result, "tools")
|
||||
if tools.IsArray() {
|
||||
toolArray := tools.Array()
|
||||
for i := 0; i < len(toolArray); i++ {
|
||||
typePath := fmt.Sprintf("tools.%d.type", i)
|
||||
result = normalizeCodexBuiltinToolAtPath(result, typePath)
|
||||
}
|
||||
}
|
||||
|
||||
result = normalizeCodexBuiltinToolAtPath(result, "tool_choice.type")
|
||||
|
||||
toolChoiceTools := gjson.GetBytes(result, "tool_choice.tools")
|
||||
if toolChoiceTools.IsArray() {
|
||||
toolArray := toolChoiceTools.Array()
|
||||
for i := 0; i < len(toolArray); i++ {
|
||||
typePath := fmt.Sprintf("tool_choice.tools.%d.type", i)
|
||||
result = normalizeCodexBuiltinToolAtPath(result, typePath)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func normalizeCodexBuiltinToolAtPath(rawJSON []byte, path string) []byte {
|
||||
currentType := gjson.GetBytes(rawJSON, path).String()
|
||||
normalizedType := normalizeCodexBuiltinToolType(currentType)
|
||||
if normalizedType == "" {
|
||||
return rawJSON
|
||||
}
|
||||
|
||||
updated, err := sjson.SetBytes(rawJSON, path, normalizedType)
|
||||
if err != nil {
|
||||
return rawJSON
|
||||
}
|
||||
|
||||
log.Debugf("codex responses: normalized builtin tool type at %s from %q to %q", path, currentType, normalizedType)
|
||||
return updated
|
||||
}
|
||||
|
||||
// normalizeCodexBuiltinToolType centralizes the current known Codex Responses
|
||||
// built-in tool alias compatibility. If Codex introduces more legacy aliases,
|
||||
// extend this helper instead of adding path-specific rewrite logic elsewhere.
|
||||
func normalizeCodexBuiltinToolType(toolType string) string {
|
||||
switch toolType {
|
||||
case "web_search_preview", "web_search_preview_2025_03_11":
|
||||
return "web_search"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,52 @@ func TestConvertSystemRoleToDeveloper_AssistantRole(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertOpenAIResponsesRequestToCodex_NormalizesWebSearchPreview(t *testing.T) {
|
||||
inputJSON := []byte(`{
|
||||
"model": "gpt-5.4-mini",
|
||||
"input": "find latest OpenAI model news",
|
||||
"tools": [
|
||||
{"type": "web_search_preview_2025_03_11"}
|
||||
],
|
||||
"tool_choice": {
|
||||
"type": "allowed_tools",
|
||||
"tools": [
|
||||
{"type": "web_search_preview"},
|
||||
{"type": "web_search_preview_2025_03_11"}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
output := ConvertOpenAIResponsesRequestToCodex("gpt-5.4-mini", inputJSON, false)
|
||||
|
||||
if got := gjson.GetBytes(output, "tools.0.type").String(); got != "web_search" {
|
||||
t.Fatalf("tools.0.type = %q, want %q: %s", got, "web_search", string(output))
|
||||
}
|
||||
if got := gjson.GetBytes(output, "tool_choice.type").String(); got != "allowed_tools" {
|
||||
t.Fatalf("tool_choice.type = %q, want %q: %s", got, "allowed_tools", string(output))
|
||||
}
|
||||
if got := gjson.GetBytes(output, "tool_choice.tools.0.type").String(); got != "web_search" {
|
||||
t.Fatalf("tool_choice.tools.0.type = %q, want %q: %s", got, "web_search", string(output))
|
||||
}
|
||||
if got := gjson.GetBytes(output, "tool_choice.tools.1.type").String(); got != "web_search" {
|
||||
t.Fatalf("tool_choice.tools.1.type = %q, want %q: %s", got, "web_search", string(output))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertOpenAIResponsesRequestToCodex_NormalizesTopLevelToolChoicePreviewAlias(t *testing.T) {
|
||||
inputJSON := []byte(`{
|
||||
"model": "gpt-5.4-mini",
|
||||
"input": "find latest OpenAI model news",
|
||||
"tool_choice": {"type": "web_search_preview_2025_03_11"}
|
||||
}`)
|
||||
|
||||
output := ConvertOpenAIResponsesRequestToCodex("gpt-5.4-mini", inputJSON, false)
|
||||
|
||||
if got := gjson.GetBytes(output, "tool_choice.type").String(); got != "web_search" {
|
||||
t.Fatalf("tool_choice.type = %q, want %q: %s", got, "web_search", string(output))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserFieldDeletion(t *testing.T) {
|
||||
inputJSON := []byte(`{
|
||||
"model": "gpt-5.2",
|
||||
|
||||
Reference in New Issue
Block a user