package openai import ( "encoding/json" "fmt" "iop/packages/go/config" ) func validateThinkControl(req *chatCompletionRequest) error { if req.ThinkingTokenBudget != nil && *req.ThinkingTokenBudget < 0 { return fmt.Errorf("thinking_token_budget must be non-negative") } if req.ReasoningEffort != nil { eff := *req.ReasoningEffort if eff == "" { return fmt.Errorf("reasoning_effort cannot be empty when present") } if eff != "none" && eff != "low" && eff != "medium" && eff != "high" { return fmt.Errorf("reasoning_effort must be one of none, low, medium, or high") } } if req.Think != nil && !*req.Think { if req.ReasoningEffort != nil && *req.ReasoningEffort != "none" { return fmt.Errorf("think=false conflicts with reasoning_effort=%s", *req.ReasoningEffort) } } if req.ThinkingTokenBudget != nil { if req.Think != nil && !*req.Think { return fmt.Errorf("thinking_token_budget cannot be set when think=false") } if req.ReasoningEffort != nil && *req.ReasoningEffort == "none" { return fmt.Errorf("thinking_token_budget cannot be set when reasoning_effort=none") } } return nil } func applyModelCatalogGenerationPolicyToChat(req *chatCompletionRequest, entry config.ModelCatalogEntry, strictOutput bool, providerNativeThinking bool) { if req == nil { return } applyOutputTokenPolicy(&req.MaxTokens, req.MaxCompletionTokens, entry.DefaultMaxTokens, entry.MinMaxTokens) if entry.DefaultThinkingTokenBudget > 0 && !providerNativeThinking && chatRequestAllowsDefaultThinkingBudget(*req) { if req.ThinkingTokenBudget == nil { req.ThinkingTokenBudget = intPtr(entry.DefaultThinkingTokenBudget) } if strictOutput && req.Think == nil { req.Think = boolPtr(true) } } } func applyModelCatalogGenerationPolicyToResponses(req *responsesRequest, entry config.ModelCatalogEntry) { if req == nil { return } applyOutputTokenPolicy(&req.MaxOutputTokens, nil, entry.DefaultMaxTokens, entry.MinMaxTokens) } func applyOutputTokenPolicy(primary **int, fallback *int, defaultTokens, minTokens int) { if primary == nil { return } current := 0 if *primary != nil { current = **primary } else if fallback != nil { current = *fallback } next := current if next == 0 && defaultTokens > 0 { next = defaultTokens } if minTokens > 0 && (next == 0 || next < minTokens) { next = minTokens } if next > 0 && next != current { *primary = intPtr(next) } } func chatRequestAllowsDefaultThinkingBudget(req chatCompletionRequest) bool { if req.Think != nil && !*req.Think { return false } if req.ReasoningEffort != nil && *req.ReasoningEffort == "none" { return false } return true } func chatRequestHasProviderNativeThinking(rawBody []byte) bool { var raw map[string]json.RawMessage if err := json.Unmarshal(rawBody, &raw); err != nil { return false } ctkRaw, ok := raw["chat_template_kwargs"] if !ok { return false } var ctk map[string]json.RawMessage if err := json.Unmarshal(ctkRaw, &ctk); err != nil { return false } _, hasEnableThinking := ctk["enable_thinking"] _, hasBudget := ctk["thinking_token_budget"] return hasEnableThinking || hasBudget } func intPtr(v int) *int { return &v } func boolPtr(v bool) *bool { return &v } func chatRunMetadata(runMeta map[string]string, req chatCompletionRequest, outputPolicy strictOutputPolicy) map[string]string { if runMeta == nil { runMeta = make(map[string]string) } runMeta["openai_model"] = req.Model runMeta["openai_stream"] = fmt.Sprintf("%t", req.Stream) runMeta["strict_output"] = fmt.Sprintf("%t", outputPolicy.Strict) return runMeta } func (s *Server) resolveStrictOutput() bool { return s.cfg.StrictOutput } func (s *Server) resolveStrictStreamBuffer() bool { return s.cfg.StrictStreamBuffer } func (s *Server) resolveOutputPolicy(prompt string) strictOutputPolicy { policy := strictOutputPolicy{ Strict: s.resolveStrictOutput(), StreamBuffer: s.resolveStrictStreamBuffer(), } if !policy.Strict { return policy } policy.XMLCompletionTool, policy.XMLResultTag = inferXMLCompletionContract(prompt) policy.ContractInstruction = policy.XMLCompletionTool != "" return policy }