fix
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// openAIChatUsesMaxCompletionTokens reports models that reject legacy max_tokens
|
||||
// on Chat Completions (o-series + GPT-5 family including GPT-5.6).
|
||||
// See https://developers.openai.com/api/reference/resources/chat
|
||||
func openAIChatUsesMaxCompletionTokens(model string) bool {
|
||||
return openAIReasoningChatModel(model)
|
||||
}
|
||||
|
||||
// openAIChatOmitsCustomTemperature reports models that reject non-default
|
||||
// temperature (only the API default of 1 is accepted, if temperature is sent).
|
||||
func openAIChatOmitsCustomTemperature(model string) bool {
|
||||
return openAIReasoningChatModel(model)
|
||||
}
|
||||
|
||||
// openAIChatSupportsReasoningEffort reports Chat Completions models that accept
|
||||
// top-level reasoning_effort (Responses API uses reasoning.effort instead).
|
||||
func openAIChatSupportsReasoningEffort(model string) bool {
|
||||
return openAIReasoningChatModel(model)
|
||||
}
|
||||
|
||||
// openAIReasoningChatModel matches OpenAI reasoning / GPT-5(+).5/5.6 chat models
|
||||
// and common snapshot aliases (e.g. gpt-5.6-luna-2026-…).
|
||||
func openAIReasoningChatModel(model string) bool {
|
||||
m := strings.ToLower(strings.TrimSpace(model))
|
||||
if m == "" {
|
||||
return false
|
||||
}
|
||||
// Strip provider prefixes used by OpenRouter-style gateways.
|
||||
if i := strings.LastIndex(m, "/"); i >= 0 {
|
||||
m = m[i+1:]
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(m, "o1"),
|
||||
strings.HasPrefix(m, "o3"),
|
||||
strings.HasPrefix(m, "o4"),
|
||||
strings.HasPrefix(m, "gpt-5"),
|
||||
strings.HasPrefix(m, "chatgpt-o"):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// defaultReasoningEffort picks Chat Completions reasoning_effort when unset.
|
||||
// Long formula-HTML enhance uses "low" so max_completion_tokens prefers visible
|
||||
// HTML over deep hidden reasoning. Tiny probes use "none" for speed/cost.
|
||||
func defaultReasoningEffort(model string, maxTokens int, explicit string) string {
|
||||
explicit = strings.ToLower(strings.TrimSpace(explicit))
|
||||
if explicit != "" {
|
||||
return explicit
|
||||
}
|
||||
if !openAIChatSupportsReasoningEffort(model) {
|
||||
return ""
|
||||
}
|
||||
if maxTokens <= 0 || maxTokens < 512 {
|
||||
return "none"
|
||||
}
|
||||
return "low"
|
||||
}
|
||||
|
||||
// chatCompletionBody is the Chat Completions JSON body. Temperature is a pointer
|
||||
// so reasoning models can omit it entirely (omitempty).
|
||||
type chatCompletionBody struct {
|
||||
Model string `json:"model"`
|
||||
Messages []chatMessage `json:"messages"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
MaxTokens int `json:"max_tokens,omitempty"`
|
||||
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
|
||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||
}
|
||||
|
||||
// buildChatCompletionBody maps CompleteOptions onto the correct Chat Completions
|
||||
// fields for classic vs GPT-5 / o-series models.
|
||||
func buildChatCompletionBody(model string, messages []chatMessage, temperature float64, maxTokens int, reasoningEffort string) chatCompletionBody {
|
||||
body := chatCompletionBody{
|
||||
Model: model,
|
||||
Messages: messages,
|
||||
}
|
||||
if openAIChatUsesMaxCompletionTokens(model) {
|
||||
if maxTokens > 0 {
|
||||
body.MaxCompletionTokens = maxTokens
|
||||
}
|
||||
} else if maxTokens > 0 {
|
||||
body.MaxTokens = maxTokens
|
||||
}
|
||||
if !openAIChatOmitsCustomTemperature(model) {
|
||||
t := temperature
|
||||
body.Temperature = &t
|
||||
}
|
||||
body.ReasoningEffort = defaultReasoningEffort(model, maxTokens, reasoningEffort)
|
||||
return body
|
||||
}
|
||||
Reference in New Issue
Block a user