feat: Hide raw tool call JSON from output_text in OpenAI-style responses when structured tool calls are present.

This commit is contained in:
CJACK
2026-02-19 00:28:44 +08:00
parent df9aea194c
commit d21aedac83
3 changed files with 82 additions and 4 deletions

View File

@@ -43,8 +43,12 @@ func BuildOpenAIChatCompletion(completionID, model, finalPrompt, finalThinking,
func BuildOpenAIResponseObject(responseID, model, finalPrompt, finalThinking, finalText string, toolNames []string) map[string]any {
detected := ParseToolCalls(finalText, toolNames)
exposedOutputText := finalText
output := make([]any, 0, 2)
if len(detected) > 0 {
// Keep structured tool output only; avoid leaking raw tool-call JSON
// into response.output_text for clients reading completed responses.
exposedOutputText = ""
toolCalls := make([]any, 0, len(detected))
for _, tc := range detected {
toolCalls = append(toolCalls, map[string]any{
@@ -88,7 +92,7 @@ func BuildOpenAIResponseObject(responseID, model, finalPrompt, finalThinking, fi
"status": "completed",
"model": model,
"output": output,
"output_text": finalText,
"output_text": exposedOutputText,
"usage": map[string]any{
"input_tokens": promptTokens,
"output_tokens": reasoningTokens + completionTokens,

View File

@@ -54,6 +54,28 @@ func TestBuildOpenAIResponseObjectWithText(t *testing.T) {
}
}
func TestBuildOpenAIResponseObjectToolCallsHidesRawOutputText(t *testing.T) {
out := BuildOpenAIResponseObject(
"resp_2",
"gpt-4o",
"prompt",
"",
`{"tool_calls":[{"name":"search","input":{"q":"go"}}]}`,
[]string{"search"},
)
if out["output_text"] != "" {
t.Fatalf("expected empty output_text for tool_calls, got %#v", out["output_text"])
}
output, _ := out["output"].([]any)
if len(output) == 0 {
t.Fatalf("expected output entries")
}
first, _ := output[0].(map[string]any)
if first["type"] != "tool_calls" {
t.Fatalf("expected first output type tool_calls, got %#v", first["type"])
}
}
func TestBuildClaudeMessageResponseToolUse(t *testing.T) {
out := BuildClaudeMessageResponse(
"msg_1",