Skip to content

Commit 8a0edd0

Browse files
authored
Always populate ORItemParam.Summary (#9049)
* fix(openresponses): do not omit required fields summary and id * fix(openresponses): ensure ORItemParam.Summary is never null Normalize Summary to an empty slice at serialization chokepoints (sendSSEEvent, bufferEvent, buildORResponse) so it always serializes as [] instead of null. Closes #9047
1 parent 35d509d commit 8a0edd0

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

core/http/endpoints/openresponses/responses.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,7 @@ func handleBackgroundStream(ctx context.Context, store *ResponseStore, responseI
13321332

13331333
// bufferEvent stores an SSE event in the response store for streaming resume
13341334
func bufferEvent(store *ResponseStore, responseID string, event *schema.ORStreamEvent) {
1335+
normalizeORStreamEvent(event)
13351336
if err := store.AppendEvent(responseID, event); err != nil {
13361337
xlog.Error("Failed to buffer event", "response_id", responseID, "error", err)
13371338
}
@@ -2605,6 +2606,7 @@ func handleOpenResponsesStream(c echo.Context, responseID string, createdAt int6
26052606

26062607
// sendSSEEvent sends a Server-Sent Event
26072608
func sendSSEEvent(c echo.Context, event *schema.ORStreamEvent) {
2609+
normalizeORStreamEvent(event)
26082610
data, err := json.Marshal(event)
26092611
if err != nil {
26102612
xlog.Error("Failed to marshal SSE event", "error", err)
@@ -2613,6 +2615,13 @@ func sendSSEEvent(c echo.Context, event *schema.ORStreamEvent) {
26132615
fmt.Fprintf(c.Response().Writer, "event: %s\ndata: %s\n\n", event.Type, string(data))
26142616
}
26152617

2618+
// normalizeORStreamEvent ensures required fields like Summary are never null.
2619+
func normalizeORStreamEvent(event *schema.ORStreamEvent) {
2620+
if event.Item != nil && event.Item.Summary == nil {
2621+
event.Item.Summary = []schema.ORContentPart{}
2622+
}
2623+
}
2624+
26162625
// getTopLogprobs returns the top_logprobs value, defaulting to 0 if nil
26172626
func getTopLogprobs(topLogprobs *int) int {
26182627
if topLogprobs != nil {
@@ -2693,6 +2702,13 @@ func buildORResponse(responseID string, createdAt int64, completedAt *int64, sta
26932702
outputItems = []schema.ORItemField{}
26942703
}
26952704

2705+
// Ensure Summary is never null on any output item
2706+
for i := range outputItems {
2707+
if outputItems[i].Summary == nil {
2708+
outputItems[i].Summary = []schema.ORContentPart{}
2709+
}
2710+
}
2711+
26962712
// Ensure tools is never null - always an array
26972713
tools := input.Tools
26982714
if tools == nil {

core/schema/openresponses.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ type ORReasoningParam struct {
8686
// ORItemParam represents an input/output item (discriminated union by type)
8787
type ORItemParam struct {
8888
Type string `json:"type"` // message|function_call|function_call_output|reasoning|item_reference
89-
ID string `json:"id,omitempty"` // Present for all output items
89+
ID string `json:"id"` // Present for all output items
9090
Status string `json:"status,omitempty"` // in_progress|completed|incomplete
9191

9292
// Message fields
@@ -102,8 +102,8 @@ type ORItemParam struct {
102102
Output interface{} `json:"output,omitempty"` // string or []ORContentPart
103103

104104
// Reasoning fields (for type == "reasoning")
105-
Summary []ORContentPart `json:"summary,omitempty"` // Array of summary parts
106-
EncryptedContent *string `json:"encrypted_content,omitempty"` // Provider-specific encrypted content
105+
Summary []ORContentPart `json:"summary"` // Array of summary parts
106+
EncryptedContent *string `json:"encrypted_content,omitempty"` // Provider-specific encrypted content
107107

108108
// Note: For item_reference type, use the ID field above to reference the item
109109
// Note: For reasoning type, Content field (from message fields) contains the raw reasoning content

0 commit comments

Comments
 (0)