From a3e21df81488587475a5392be8300dd563e6b9c8 Mon Sep 17 00:00:00 2001 From: apparition <38576169+possible055@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:33:16 +0800 Subject: [PATCH] fix(openai): avoid developer transcript resets - Narrow websocket transcript replacement detection to assistant outputs and function calls - Preserve existing merge behavior for follow-up developer messages without previous_response_id - Add a regression test covering mid-session developer message updates --- .../openai/openai_responses_websocket.go | 2 +- .../openai/openai_responses_websocket_test.go | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/sdk/api/handlers/openai/openai_responses_websocket.go b/sdk/api/handlers/openai/openai_responses_websocket.go index 211b8b81..15a6bda7 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket.go +++ b/sdk/api/handlers/openai/openai_responses_websocket.go @@ -374,7 +374,7 @@ func shouldReplaceWebsocketTranscript(rawJSON []byte, nextInput gjson.Result) bo return true case "message": role := strings.TrimSpace(item.Get("role").String()) - if role == "assistant" || role == "developer" { + if role == "assistant" { return true } } diff --git a/sdk/api/handlers/openai/openai_responses_websocket_test.go b/sdk/api/handlers/openai/openai_responses_websocket_test.go index b1440a95..5619e6b1 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket_test.go +++ b/sdk/api/handlers/openai/openai_responses_websocket_test.go @@ -741,6 +741,32 @@ func TestNormalizeResponsesWebsocketRequestTreatsTranscriptReplacementAsReset(t } } +func TestNormalizeResponsesWebsocketRequestDoesNotTreatDeveloperMessageAsReplacement(t *testing.T) { + lastRequest := []byte(`{"model":"test-model","stream":true,"input":[{"type":"message","id":"msg-1"}]}`) + lastResponseOutput := []byte(`[ + {"type":"message","id":"assistant-1","role":"assistant"} + ]`) + raw := []byte(`{"type":"response.create","input":[{"type":"message","id":"dev-1","role":"developer"},{"type":"message","id":"msg-2"}]}`) + + normalized, next, errMsg := normalizeResponsesWebsocketRequest(raw, lastRequest, lastResponseOutput) + if errMsg != nil { + t.Fatalf("unexpected error: %v", errMsg.Error) + } + items := gjson.GetBytes(normalized, "input").Array() + if len(items) != 4 { + t.Fatalf("merged input len = %d, want 4: %s", len(items), normalized) + } + if items[0].Get("id").String() != "msg-1" || + items[1].Get("id").String() != "assistant-1" || + items[2].Get("id").String() != "dev-1" || + items[3].Get("id").String() != "msg-2" { + t.Fatalf("developer follow-up should preserve merge behavior: %s", normalized) + } + if !bytes.Equal(next, normalized) { + t.Fatalf("next request snapshot should match merged request") + } +} + func TestResponsesWebsocketCompactionResetsTurnStateOnTranscriptReplacement(t *testing.T) { gin.SetMode(gin.TestMode)