package clients import ( "fmt" "net/http" "sort" ) // Keep these lists aligned with openai-node 6.47.0 and the public OpenAI API // reference. The Gateway accepts a small, explicit set of routing extensions at // ingress, but only protocol fields (plus controlled provider adaptations) are // allowed across the upstream boundary. var openAIChatRequestParameters = stringSet( "messages", "model", "audio", "frequency_penalty", "function_call", "functions", "logit_bias", "logprobs", "max_completion_tokens", "max_tokens", "metadata", "modalities", "moderation", "n", "parallel_tool_calls", "prediction", "presence_penalty", "prompt_cache_key", "prompt_cache_options", "prompt_cache_retention", "reasoning_effort", "response_format", "safety_identifier", "seed", "service_tier", "stop", "store", "stream", "stream_options", "temperature", "tool_choice", "tools", "top_logprobs", "top_p", "user", "verbosity", "web_search_options", ) var openAIResponsesRequestParameters = stringSet( "background", "context_management", "conversation", "include", "input", "instructions", "max_output_tokens", "max_tool_calls", "metadata", "model", "moderation", "parallel_tool_calls", "previous_response_id", "prompt", "prompt_cache_key", "prompt_cache_options", "prompt_cache_retention", "reasoning", "safety_identifier", "service_tier", "store", "stream", "stream_options", "temperature", "text", "tool_choice", "tools", "top_logprobs", "top_p", "truncation", "user", ) var gatewayOpenAIRequestExtensions = stringSet( "runMode", "run_mode", "conversationId", "conversation_id", "sessionId", "session_id", "requestId", "request_id", "signal", "userMessage", "user_message", "platformId", "platform_id", "options", "enable_thinking", "thinking_budget_tokens", "enable_web_search", "modelType", "model_type", "capability", "capabilityType", "mode", "simulation", "testMode", ) var gatewayResponsesRequestExtensions = stringSet("messages", "presence_penalty", "frequency_penalty") var controlledOpenAIChatProviderParameters = stringSet( "enable_thinking", "thinking_budget", "thinking", "enable_web_search", ) var controlledOpenAIResponsesProviderParameters = stringSet("presence_penalty", "frequency_penalty") func ValidateOpenAIRequestParameters(kind string, body map[string]any) error { allowed := openAIChatRequestParameters if kind == "responses" { allowed = openAIResponsesRequestParameters } unknown := make([]string, 0) for key := range body { if _, ok := allowed[key]; ok { continue } if _, ok := gatewayOpenAIRequestExtensions[key]; ok { continue } if kind == "responses" { if _, ok := gatewayResponsesRequestExtensions[key]; ok { continue } } unknown = append(unknown, key) } if len(unknown) == 0 { return nil } sort.Strings(unknown) return &ClientError{ Code: "invalid_parameter", Message: fmt.Sprintf("Unknown parameter: %s", unknown[0]), Param: unknown[0], StatusCode: http.StatusBadRequest, Retryable: false, } } func FilterOpenAIChatRequestBody(body map[string]any) map[string]any { return filterOpenAIRequestBody(body, openAIChatRequestParameters, controlledOpenAIChatProviderParameters) } func FilterOpenAIResponsesRequestBody(body map[string]any) map[string]any { return filterOpenAIRequestBody(body, openAIResponsesRequestParameters, controlledOpenAIResponsesProviderParameters) } func filterOpenAIRequestBody(body map[string]any, allowed map[string]struct{}, extensions map[string]struct{}) map[string]any { out := make(map[string]any, len(body)) for key, value := range body { if _, ok := allowed[key]; ok { out[key] = value continue } if _, ok := extensions[key]; ok { out[key] = value } } return out } func stringSet(values ...string) map[string]struct{} { out := make(map[string]struct{}, len(values)) for _, value := range values { out[value] = struct{}{} } return out }