From 379167c992700f453bff48b2b4e51bb309d47532 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 20 Jun 2026 11:10:53 +0800 Subject: [PATCH] feat(translator): add benchmarking for `convertSystemRoleToDeveloper` with large inputs - Introduced `BenchmarkConvertSystemRoleToDeveloperLargeInput` to evaluate performance on various input sizes and configurations. - Improved `convertSystemRoleToDeveloper` logic to optimize JSON rebuilding and reduce unnecessary operations. - Created helper `makeLargeResponsesInputForBenchmark` to generate test data for performance scenarios. Closes: #3751 --- .../codex_openai-responses_request.go | 37 +++++-- .../codex_openai-responses_request_test.go | 104 ++++++++++++++++++ 2 files changed, 133 insertions(+), 8 deletions(-) diff --git a/internal/translator/codex/openai/responses/codex_openai-responses_request.go b/internal/translator/codex/openai/responses/codex_openai-responses_request.go index cc218b12..be0383bc 100644 --- a/internal/translator/codex/openai/responses/codex_openai-responses_request.go +++ b/internal/translator/codex/openai/responses/codex_openai-responses_request.go @@ -1,6 +1,7 @@ package responses import ( + "encoding/json" "fmt" log "github.com/sirupsen/logrus" @@ -71,18 +72,38 @@ func convertSystemRoleToDeveloper(rawJSON []byte) []byte { return rawJSON } - inputArray := inputResult.Array() - result := rawJSON + inputItems := inputResult.Array() + if len(inputItems) == 0 { + return rawJSON + } - // Directly modify role values for items with "system" role - for i := 0; i < len(inputArray); i++ { - rolePath := fmt.Sprintf("input.%d.role", i) - if gjson.GetBytes(result, rolePath).String() == "system" { - result, _ = sjson.SetBytes(result, rolePath, "developer") + changed := false + rebuiltInput := make([]json.RawMessage, 0, len(inputItems)) + for _, item := range inputItems { + itemRaw := []byte(item.Raw) + if item.IsObject() && item.Get("role").String() == "system" { + updatedItem, errSetItem := sjson.SetRawBytes(itemRaw, "role", []byte(`"developer"`)) + if errSetItem != nil { + return rawJSON + } + itemRaw = updatedItem + changed = true } + rebuiltInput = append(rebuiltInput, json.RawMessage(itemRaw)) + } + if !changed { + return rawJSON } - return result + inputRaw, errMarshalInput := json.Marshal(rebuiltInput) + if errMarshalInput != nil { + return rawJSON + } + updated, errSetInput := sjson.SetRawBytes(rawJSON, "input", inputRaw) + if errSetInput != nil { + return rawJSON + } + return updated } // normalizeCodexBuiltinTools rewrites legacy/preview built-in tool variants to the diff --git a/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go b/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go index 3b48a76e..7b0ebadb 100644 --- a/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go +++ b/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go @@ -1,11 +1,17 @@ package responses import ( + "fmt" + "strconv" + "strings" "testing" "github.com/tidwall/gjson" + "github.com/tidwall/sjson" ) +var benchmarkConvertSystemRoleOutput []byte + // TestConvertSystemRoleToDeveloper_BasicConversion tests the basic system -> developer role conversion func TestConvertSystemRoleToDeveloper_BasicConversion(t *testing.T) { inputJSON := []byte(`{ @@ -364,3 +370,101 @@ func TestTruncationRemovedForCodexCompatibility(t *testing.T) { t.Fatalf("truncation should be removed for Codex compatibility") } } + +func BenchmarkConvertSystemRoleToDeveloperLargeInput(b *testing.B) { + cases := []struct { + name string + inputJSON []byte + }{ + { + name: "200_input_1_system", + inputJSON: makeLargeResponsesInputForBenchmark(200, 200), + }, + { + name: "200_input_2_system", + inputJSON: makeLargeResponsesInputForBenchmark(200, 100), + }, + { + name: "2000_input_20_system", + inputJSON: makeLargeResponsesInputForBenchmark(2000, 100), + }, + } + benchmarks := []struct { + name string + fn func([]byte) []byte + }{ + { + name: "previous_root_path_rewrite", + fn: convertSystemRoleToDeveloperPreviousRootPathRewriteForBenchmark, + }, + { + name: "current_rebuilt_input_json_marshal", + fn: convertSystemRoleToDeveloper, + }, + } + + for _, testCase := range cases { + for _, benchmark := range benchmarks { + b.Run(testCase.name+"/"+benchmark.name, func(b *testing.B) { + output := benchmark.fn(testCase.inputJSON) + if got := gjson.GetBytes(output, "input.0.role").String(); got != "developer" { + b.Fatalf("input.0.role = %q, want %q", got, "developer") + } + if got := gjson.GetBytes(output, "input.1.role").String(); got != "user" { + b.Fatalf("input.1.role = %q, want %q", got, "user") + } + + b.ReportAllocs() + b.SetBytes(int64(len(testCase.inputJSON))) + b.ResetTimer() + + var benchmarkOutput []byte + for i := 0; i < b.N; i++ { + benchmarkOutput = benchmark.fn(testCase.inputJSON) + } + benchmarkConvertSystemRoleOutput = benchmarkOutput + }) + } + } +} + +func makeLargeResponsesInputForBenchmark(inputCount int, systemEvery int) []byte { + var builder strings.Builder + builder.Grow(inputCount * 96) + builder.WriteString(`{"model":"gpt-5.2","input":[`) + for i := 0; i < inputCount; i++ { + if i > 0 { + builder.WriteByte(',') + } + role := "user" + if i%systemEvery == 0 { + role = "system" + } + builder.WriteString(`{"type":"message","role":"`) + builder.WriteString(role) + builder.WriteString(`","content":[{"type":"input_text","text":"message `) + builder.WriteString(strconv.Itoa(i)) + builder.WriteString(`"}]}`) + } + builder.WriteString(`]}`) + return []byte(builder.String()) +} + +func convertSystemRoleToDeveloperPreviousRootPathRewriteForBenchmark(rawJSON []byte) []byte { + inputResult := gjson.GetBytes(rawJSON, "input") + if !inputResult.IsArray() { + return rawJSON + } + + inputArray := inputResult.Array() + result := rawJSON + + for i := 0; i < len(inputArray); i++ { + rolePath := fmt.Sprintf("input.%d.role", i) + if gjson.GetBytes(result, rolePath).String() == "system" { + result, _ = sjson.SetBytes(result, rolePath, "developer") + } + } + + return result +} -- 2.51.2