Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion provider/openaiprovider/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,12 @@ func responsesProcessResponse(resp *responses.Response, seqNum int64, yield func
if !yield(currentUpdate, nil) {
return
}
currentUpdate = &agent.ResponseUpdate{}
// Reset for the next message, carrying the response-level
// properties forward so the second and later messages keep
// AdditionalProperties (e.g. EndUserId).
currentUpdate = &agent.ResponseUpdate{
AdditionalProperties: responsesPopulateAdditionalProperties(resp),
}
}
currentUpdate.MessageID = out.ID
currentUpdate.ResponseID = resp.ID
Expand Down
52 changes: 52 additions & 0 deletions provider/openaiprovider/responses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6818,6 +6818,58 @@ func responsesBodyEqual(t *testing.T, got string, want string) {
}
}

// A non-streaming Responses result with more than one output message must carry
// response-level AdditionalProperties (e.g. EndUserId) on every message, not
// only the first. currentUpdate is reset to a fresh value for each message
// after the first, so the properties must be repopulated per message.
func TestResponses_NonStreaming_AllMessagesKeepAdditionalProperties(t *testing.T) {
const input = `
{
"model":"gpt-4o-mini",
"input": [{
"type":"message",
"role":"user",
"content":[{"type":"input_text","text":"hello"}]
}]
}`
const output = `
{
"id":"resp_multi",
"object":"response",
"created_at":1741891428,
"status":"completed",
"error":null,
"incomplete_details":null,
"model":"gpt-4o-mini",
"user":"end-user-42",
"output":[
{"type":"message","id":"msg_1","status":"completed","role":"assistant","content":[{"type":"output_text","text":"first","annotations":[]}]},
{"type":"message","id":"msg_2","status":"completed","role":"assistant","content":[{"type":"output_text","text":"second","annotations":[]}]}
]
}`

server := newTestResponsesServer(t, input, output)
defer server.Close()
a := newTestResponsesClient(server, "gpt-4o-mini")

var msgUpdates int
for u, err := range a.RunText(t.Context(), "hello") {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if u.MessageID == "" {
continue
}
msgUpdates++
if got, _ := u.AdditionalProperties["EndUserId"].(string); got != "end-user-42" {
t.Errorf("message %q: EndUserId = %q, want %q", u.MessageID, got, "end-user-42")
}
}
if msgUpdates != 2 {
t.Fatalf("expected 2 message-bearing updates, got %d", msgUpdates)
}
}

// TestResponsesStreamingFailedResponseSurfacesError verifies that a streamed
// response.failed event surfaces its error as ErrorContent rather than an
// empty update.
Expand Down
Loading