148 lines
4.1 KiB
Go
148 lines
4.1 KiB
Go
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
|
|
}
|