package service import ( "fmt" "strings" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) func openAIResponsesInputItemIDPrefix(itemType string) (string, bool) { switch strings.TrimSpace(itemType) { case "message": return "msg", true case "reasoning": return "rs", true case "web_search_call": return "ws", true case "custom_tool_call": return openAIResponsesToolCallIDPrefix(itemType), true case "tool_search_call": return openAIResponsesToolCallIDPrefix(itemType), true case "custom_tool_call_output": // Although custom calls use ctc IDs, OpenAI validates replayed custom // call output item IDs against the generic fc namespace. return "fc", true default: if isCodexToolCallInputType(itemType) { return openAIResponsesToolCallIDPrefix(itemType), true } return "", false } } func openAIResponsesToolCallIDPrefix(itemType string) string { switch strings.TrimSpace(itemType) { case "custom_tool_call", "custom_tool_call_output": return "ctc" case "tool_search_call", "tool_search_output": return "tsc" default: return "fc" } } // Invalid replayed IDs are removed rather than rewritten because a fabricated // ID may point at a different upstream object. func shouldStripOpenAIResponsesInputItemID(itemType, id string) bool { prefix, constrained := openAIResponsesInputItemIDPrefix(itemType) if !constrained { return false } return id == "" || !strings.HasPrefix(id, prefix) } func shouldStripOpenAIResponsesNonPairCallID(itemType string) bool { switch strings.TrimSpace(itemType) { case "message", "reasoning", "image_generation_call": return true default: return false } } func sanitizeOpenAIResponsesInputItemIDs(body []byte) ([]byte, bool, error) { input := gjson.GetBytes(body, "input") if !input.IsArray() { return body, false, nil } type inputItem struct { body []byte stripID bool stripCallID bool } items := make([]inputItem, 0) input.ForEach(func(_, item gjson.Result) bool { parsed := inputItem{body: []byte(item.Raw)} if item.IsObject() { itemType := item.Get("type") id := item.Get("id") trimmedItemType := strings.TrimSpace(itemType.String()) parsed.stripCallID = item.Get("call_id").Exists() && shouldStripOpenAIResponsesNonPairCallID(trimmedItemType) if id.Type == gjson.String { parsed.stripID = shouldStripOpenAIResponsesInputItemID(trimmedItemType, id.String()) } } items = append(items, parsed) return true }) hasSanitization := false for _, item := range items { if item.stripID || item.stripCallID { hasSanitization = true break } } if !hasSanitization { return body, false, nil } rebuiltItems := make([][]byte, 0, len(items)) for index, item := range items { itemBody := item.body if item.stripID { var err error itemBody, err = sjson.DeleteBytes(itemBody, "id") if err != nil { return nil, false, fmt.Errorf("delete input.%d.id: %w", index, err) } } if item.stripCallID { var err error itemBody, err = sjson.DeleteBytes(itemBody, "call_id") if err != nil { return nil, false, fmt.Errorf("delete input.%d.call_id: %w", index, err) } } rebuiltItems = append(rebuiltItems, itemBody) } rebuiltInput := make([]byte, 0, len(input.Raw)) rebuiltInput = append(rebuiltInput, '[') for i, item := range rebuiltItems { if i > 0 { rebuiltInput = append(rebuiltInput, ',') } rebuiltInput = append(rebuiltInput, item...) } rebuiltInput = append(rebuiltInput, ']') sanitized, err := sjson.SetRawBytes(body, "input", rebuiltInput) if err != nil { return nil, false, fmt.Errorf("replace sanitized input: %w", err) } return sanitized, true, nil }