Files
sub2api/backend/internal/service/openai_responses_input_compat.go
T

87 lines
2.8 KiB
Go

package service
import (
"strings"
)
const openAIResponsesInputTextMaxChars = 10000000
// sanitizeOpenAIResponsesOrphanToolOutputs removes tool-output items that have
// no matching call or item reference anywhere in the current input. Named
// function outputs without a call ID are standalone inputs, not orphan results.
func sanitizeOpenAIResponsesOrphanToolOutputs(reqBody map[string]any, input []any, hasPreviousResponseID bool) bool {
if len(input) == 0 || hasPreviousResponseID {
return false
}
toolCallIDs := make(map[string]struct{}, len(input))
referenceIDs := make(map[string]struct{}, len(input))
for _, rawItem := range input {
item, ok := rawItem.(map[string]any)
if !ok {
continue
}
itemType := strings.TrimSpace(firstNonEmptyString(item["type"]))
if itemType == "item_reference" {
if id := strings.TrimSpace(firstNonEmptyString(item["id"])); id != "" {
referenceIDs[id] = struct{}{}
}
continue
}
if !isCodexToolCallContextItemType(itemType) {
continue
}
if id := strings.TrimSpace(firstNonEmptyString(item["call_id"], item["id"])); id != "" {
toolCallIDs[id] = struct{}{}
}
}
modified := false
normalized := make([]any, 0, len(input))
for _, rawItem := range input {
item, ok := rawItem.(map[string]any)
if !ok || !isCodexToolCallOutputItemType(strings.TrimSpace(firstNonEmptyString(item["type"]))) {
normalized = append(normalized, rawItem)
continue
}
callID := strings.TrimSpace(firstNonEmptyString(item["call_id"]))
// Codex sends externally supplied inputs (such as task delegation) as
// named function outputs without a preceding function call. Preserve
// their native type, namespace and output instead of silently dropping
// the request that started the turn.
if callID == "" && strings.TrimSpace(firstNonEmptyString(item["type"])) == "function_call_output" &&
strings.TrimSpace(firstNonEmptyString(item["name"])) != "" {
normalized = append(normalized, rawItem)
continue
}
_, hasToolCall := toolCallIDs[callID]
_, hasReference := referenceIDs[callID]
if callID != "" && (hasToolCall || hasReference) {
normalized = append(normalized, rawItem)
continue
}
modified = true
}
if !modified {
return false
}
reqBody["input"] = normalized
return true
}
func truncateOpenAIResponsesInputText(_ map[string]any) bool {
// Do not silently rewrite client or tool output. If an upstream enforces a
// text limit, forwarding the original value preserves its explicit error for
// the client and the normal Ops error pipeline. This compatibility shim is
// retained until the two callers can remove the old mutation hook together.
return false
}
func openAIResponsesInputMayNeedTruncation(_ []byte) bool {
// See truncateOpenAIResponsesInputText. Returning false also avoids decoding
// very large bodies solely for a mutation that must not happen.
return false
}