// Package responses describes the OpenAI Responses API that Codex // speaks and converts it to and from Chat Completions. package responses import ( "bytes" "encoding/json" "strings" ) // Request is the Responses API request body. Only the fields CrossRelay // understands are modelled; everything else is ignored. type Request struct { Model string `json:"model"` Instructions string `json:"instructions,omitempty"` Input json.RawMessage `json:"input"` Tools []Tool `json:"tools,omitempty"` ToolChoice json.RawMessage `json:"tool_choice,omitempty"` MaxOutputTokens int `json:"max_output_tokens,omitempty"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` Stream bool `json:"stream,omitempty"` } // Tool is one tool definition. Type is "function" for regular tools; // "custom" and "local_shell" are freeform forms Codex may send. type Tool struct { Type string `json:"type"` Name string `json:"name"` Description string `json:"description,omitempty"` Parameters json.RawMessage `json:"parameters,omitempty"` } // Item is one entry of the input array. Only the fields belonging to // Type are populated. type Item struct { Type string `json:"type"` // message | function_call | function_call_output | reasoning Role string `json:"role,omitempty"` // message Content json.RawMessage `json:"content,omitempty"` // string or []ContentPart // function_call CallID string `json:"call_id,omitempty"` Name string `json:"name,omitempty"` Arguments string `json:"arguments,omitempty"` // function_call_output Output json.RawMessage `json:"output,omitempty"` // reasoning Summary json.RawMessage `json:"summary,omitempty"` } // ContentPart is one part of a message item's content. type ContentPart struct { Type string `json:"type"` // input_text | output_text | input_image Text string `json:"text,omitempty"` // input_image ImageURL string `json:"image_url,omitempty"` } // Response is the Responses API response body. type Response struct { ID string `json:"id"` Object string `json:"object"` CreatedAt int64 `json:"created_at"` Status string `json:"status"` Model string `json:"model"` Output []OutItem `json:"output"` OutputText string `json:"output_text,omitempty"` Usage *Usage `json:"usage,omitempty"` IncompleteDetails *Incomplete `json:"incomplete_details,omitempty"` Error *RespError `json:"error,omitempty"` } // OutItem is one output entry: an assistant message or a function call. type OutItem struct { ID string `json:"id,omitempty"` Type string `json:"type"` // message | function_call Role string `json:"role,omitempty"` Status string `json:"status,omitempty"` Content []ContentPart `json:"content,omitempty"` CallID string `json:"call_id,omitempty"` Name string `json:"name,omitempty"` Arguments string `json:"arguments,omitempty"` } // Usage reports token counts. type Usage struct { InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"` TotalTokens int `json:"total_tokens"` } // IncompleteDetails explains a non-completed response. type Incomplete struct { Reason string `json:"reason"` } // RespError is the error payload of a failed response. type RespError struct { Code string `json:"code"` Message string `json:"message"` } // ErrorResponse is the Responses API error envelope. type ErrorResponse struct { Error Detail `json:"error"` } // Detail is the error payload. type Detail struct { Type string `json:"type"` Message string `json:"message"` } // ParseInput decodes the input field, which is either a plain string // or an array of items. func ParseInput(raw json.RawMessage) ([]Item, error) { trimmed := bytes.TrimSpace(raw) if len(trimmed) == 0 || string(trimmed) == "null" { return nil, nil } if trimmed[0] == '"' { var s string if err := json.Unmarshal(trimmed, &s); err != nil { return nil, err } if s == "" { return nil, nil } return []Item{{Type: "message", Role: "user", Content: trimmed}}, nil } var items []Item if err := json.Unmarshal(trimmed, &items); err != nil { return nil, err } return items, nil } // ParseContent decodes a message item's content field into parts. A // plain string becomes a single text part. func ParseContent(raw json.RawMessage) ([]ContentPart, error) { trimmed := bytes.TrimSpace(raw) if len(trimmed) == 0 || string(trimmed) == "null" { return nil, nil } if trimmed[0] == '"' { var s string if err := json.Unmarshal(trimmed, &s); err != nil { return nil, err } if s == "" { return nil, nil } return []ContentPart{{Type: "input_text", Text: s}}, nil } var parts []ContentPart if err := json.Unmarshal(trimmed, &parts); err != nil { return nil, err } return parts, nil } // OutputText flattens a function_call_output output field, which is // either a plain string or an array of text parts. func OutputText(raw json.RawMessage) string { parts, err := ParseContent(raw) if err != nil { return "" } var sb strings.Builder for _, p := range parts { switch p.Type { case "output_text", "input_text", "text": sb.WriteString(p.Text) } } return sb.String() }