Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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
}
Expand Down Expand Up @@ -82,3 +83,26 @@ 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)
if gjson.GetBytes(result, typePath).String() == "web_search_preview" {
result, _ = sjson.SetBytes(result, typePath, "web_search")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sjson.SetBytes function can return an error, which is currently ignored. While the transformation might be expected to always succeed for valid JSON, explicitly handling potential errors (e.g., logging them or returning the original rawJSON if an error occurs) would make the function more robust against unexpected input structures. This could prevent silent failures where the normalization doesn't happen as intended.

result, err := sjson.SetBytes(result, typePath, "web_search")
if err != nil {
	// Log the error or handle it appropriately
	// For now, we proceed with the original result if setting fails
	// log.Printf("Error setting tool type: %v", err)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize the dated web_search_preview alias too

This comparison only catches the undated literal web_search_preview. OpenAI’s current tool schema/SDKs also emit the dated alias web_search_preview_2025_03_11; when a caller uses that value in tools (or in the identical tool_choice.type check below), the translator forwards it unchanged and Codex will still reject the request as an unsupported tool type. That leaves the new compatibility layer incomplete for one of the valid preview names clients send today.

Useful? React with 👍 / 👎.

}
}
}

if gjson.GetBytes(result, "tool_choice.type").String() == "web_search_preview" {
result, _ = sjson.SetBytes(result, "tool_choice.type", "web_search")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the previous comment, the error returned by sjson.SetBytes here is ignored. Consider adding error handling to ensure that any issues during JSON modification are either logged or gracefully managed.

result, err := sjson.SetBytes(result, "tool_choice.type", "web_search")
if err != nil {
	// Log the error or handle it appropriately
	// For now, we proceed with the original result if setting fails
	// log.Printf("Error setting tool_choice type: %v", err)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rewrite preview tools nested under tool_choice.allowed_tools

Only the top-level tool_choice.type is normalized here. Responses requests can also force a subset of tools with tool_choice: {"type":"allowed_tools","tools":[...]}; if one of those nested entries is {"type":"web_search_preview"}, it survives translation unchanged because this helper only touches the root tools[] array and tool_choice.type. In that supported tool-choice mode, Codex will continue to fail on the same legacy tool value this patch is trying to make compatible.

Useful? React with 👍 / 👎.

}

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,26 @@ 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"}
],
"tool_choice": {"type": "web_search_preview"}
}`)

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 != "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",
Expand Down
Loading