From aed54adbea6130c0360eb928e2347318b91d4cba Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 19 Jun 2026 05:48:57 +0800 Subject: [PATCH] feat(translator): preserve structured `tool_choice` in OpenAI response conversions - Updated `ConvertOpenAIResponsesRequestToOpenAIChatCompletions` to retain `tool_choice` with raw byte handling. - Added `TestConvertOpenAIResponsesRequestToOpenAIChatCompletions_PreservesStructuredToolChoice` to ensure function and type fields are preserved in transformations. Closes: #3384 --- .../openai_openai-responses_request.go | 2 +- .../openai_openai-responses_request_test.go | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/internal/translator/openai/openai/responses/openai_openai-responses_request.go b/internal/translator/openai/openai/responses/openai_openai-responses_request.go index 1cd77c4d..76b28acf 100644 --- a/internal/translator/openai/openai/responses/openai_openai-responses_request.go +++ b/internal/translator/openai/openai/responses/openai_openai-responses_request.go @@ -250,7 +250,7 @@ func ConvertOpenAIResponsesRequestToOpenAIChatCompletions(modelName string, inpu // Convert tool_choice if present if toolChoice := root.Get("tool_choice"); toolChoice.Exists() { - out, _ = sjson.SetBytes(out, "tool_choice", toolChoice.String()) + out, _ = sjson.SetRawBytes(out, "tool_choice", []byte(toolChoice.Raw)) } return out diff --git a/internal/translator/openai/openai/responses/openai_openai-responses_request_test.go b/internal/translator/openai/openai/responses/openai_openai-responses_request_test.go index c35f0e2b..56bc5686 100644 --- a/internal/translator/openai/openai/responses/openai_openai-responses_request_test.go +++ b/internal/translator/openai/openai/responses/openai_openai-responses_request_test.go @@ -173,3 +173,28 @@ func TestConvertOpenAIResponsesRequestToOpenAIChatCompletions_FlattensNamespaceT t.Fatalf("tools.0.function.parameters.required.0 = %q, want a; output=%s", got, out) } } + +func TestConvertOpenAIResponsesRequestToOpenAIChatCompletions_PreservesStructuredToolChoice(t *testing.T) { + raw := []byte(`{ + "input": [ + {"role":"user","content":"Run command."} + ], + "tool_choice": { + "type": "function", + "function": { + "name": "run_command" + } + } + }`) + t.Logf("input json:\n%s", prettyJSONForTest(raw)) + + out := ConvertOpenAIResponsesRequestToOpenAIChatCompletions("gpt-5.4", raw, false) + t.Logf("output json:\n%s", prettyJSONForTest(out)) + + if got := gjson.GetBytes(out, "tool_choice.type").String(); got != "function" { + t.Fatalf("tool_choice.type = %q, want function; output=%s", got, out) + } + if got := gjson.GetBytes(out, "tool_choice.function.name").String(); got != "run_command" { + t.Fatalf("tool_choice.function.name = %q, want run_command; output=%s", got, out) + } +} -- 2.51.2