feat: publish v1.0.0-rc.36-custom.1
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
This commit is contained in:
commit
bb7968b612
2381 files changed
+551494
No files matched your search
@@ -0,0 +1,278 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning"
|
||||
"github.com/QuantumNous/new-api/relaykit/types"
|
||||
)
|
||||
|
||||
type parsedModelModifiers struct {
|
||||
base string
|
||||
hasSyntax bool
|
||||
intent reasoning.Intent
|
||||
hasThinking bool
|
||||
temperature *float64
|
||||
topP *float64
|
||||
hasTemperature bool
|
||||
hasTopP bool
|
||||
diagnostics []types.ConversionDiagnostic
|
||||
}
|
||||
|
||||
const modelModifierExemptionHint = `If this segment is part of the real model name, add the model to the "Models that skip thinking suffix processing" setting (re: regex entries are supported)`
|
||||
|
||||
func modelModifierClientError(message string) error {
|
||||
return fmt.Errorf("%s. %s", message, modelModifierExemptionHint)
|
||||
}
|
||||
|
||||
func parseExplicitModelModifiers(modelName string) (parsedModelModifiers, error) {
|
||||
spec := reasoning.ParseModelModifiers(modelName)
|
||||
parsed := parsedModelModifiers{base: spec.Base, hasSyntax: spec.HasModifiers()}
|
||||
last := make(map[string]int, len(spec.Modifiers))
|
||||
|
||||
for index, modifier := range spec.Modifiers {
|
||||
if _, duplicate := last[modifier.Key]; duplicate {
|
||||
parsed.diagnostics = append(parsed.diagnostics, modelModifierDiagnostic(
|
||||
"duplicate_model_modifier",
|
||||
modifier.Key,
|
||||
fmt.Sprintf("model modifier %q is repeated; the rightmost value is used", modifier.Key),
|
||||
))
|
||||
}
|
||||
last[modifier.Key] = index
|
||||
}
|
||||
|
||||
for index, modifier := range spec.Modifiers {
|
||||
if last[modifier.Key] != index {
|
||||
continue
|
||||
}
|
||||
switch modifier.Key {
|
||||
case "thinking":
|
||||
intent, ok := reasoning.ParseThinkingModifier(modifier.Value)
|
||||
if !ok {
|
||||
return parsedModelModifiers{}, modelModifierClientError(
|
||||
fmt.Sprintf("invalid thinking modifier value %q", modifier.Value),
|
||||
)
|
||||
}
|
||||
if parsed.hasThinking && intent.Mode != reasoning.ModeDisabled && intent.BudgetTokens == nil {
|
||||
parsed.intent.Mode = intent.Mode
|
||||
parsed.intent.Source = reasoning.SourceSuffix
|
||||
} else if parsed.hasThinking && intent.Mode != reasoning.ModeDisabled {
|
||||
parsed.intent.Mode = intent.Mode
|
||||
parsed.intent.BudgetTokens = intent.BudgetTokens
|
||||
parsed.intent.BudgetSource = intent.BudgetSource
|
||||
parsed.intent.Source = reasoning.SourceSuffix
|
||||
} else {
|
||||
parsed.intent = intent
|
||||
}
|
||||
parsed.hasThinking = true
|
||||
case "effort":
|
||||
effort, err := reasoning.ParseEffort(modifier.Value)
|
||||
if err != nil || effort == "" {
|
||||
return parsedModelModifiers{}, modelModifierClientError(
|
||||
fmt.Sprintf("invalid effort modifier value %q: must be one of none/low/medium/high/xhigh/max", modifier.Value),
|
||||
)
|
||||
}
|
||||
if effort == reasoning.EffortNone {
|
||||
parsed.intent = reasoning.Intent{Mode: reasoning.ModeDisabled, Effort: reasoning.EffortNone, Source: reasoning.SourceSuffix}
|
||||
} else {
|
||||
if parsed.intent.Mode == reasoning.ModeUnset || parsed.intent.Mode == reasoning.ModeDisabled {
|
||||
parsed.intent.Mode = reasoning.ModeEnabled
|
||||
}
|
||||
parsed.intent.Effort = effort
|
||||
parsed.intent.Source = reasoning.SourceSuffix
|
||||
}
|
||||
parsed.hasThinking = true
|
||||
case "temperature":
|
||||
value, ok := parseFiniteFloat(modifier.Value)
|
||||
if !ok {
|
||||
return parsedModelModifiers{}, modelModifierClientError(
|
||||
fmt.Sprintf("invalid temperature modifier value %q: must be a finite number", modifier.Value),
|
||||
)
|
||||
}
|
||||
parsed.temperature = &value
|
||||
parsed.hasTemperature = true
|
||||
case "topp":
|
||||
value, ok := parseFiniteFloat(modifier.Value)
|
||||
if !ok {
|
||||
return parsedModelModifiers{}, modelModifierClientError(
|
||||
fmt.Sprintf("invalid topp modifier value %q: must be a finite number", modifier.Value),
|
||||
)
|
||||
}
|
||||
parsed.topP = &value
|
||||
parsed.hasTopP = true
|
||||
default:
|
||||
return parsedModelModifiers{}, modelModifierClientError(
|
||||
fmt.Sprintf("unsupported model modifier %q", modifier.Key),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func parseFiniteFloat(raw string) (float64, bool) {
|
||||
value, err := strconv.ParseFloat(strings.TrimSpace(raw), 64)
|
||||
if err != nil || math.IsNaN(value) || math.IsInf(value, 0) {
|
||||
return 0, false
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
|
||||
func extractTemperature(req dto.Request) (float64, bool) {
|
||||
switch request := req.(type) {
|
||||
case *dto.GeneralOpenAIRequest:
|
||||
if request != nil && request.Temperature != nil {
|
||||
return *request.Temperature, true
|
||||
}
|
||||
case *dto.OpenAIResponsesRequest:
|
||||
if request != nil && request.Temperature != nil {
|
||||
return *request.Temperature, true
|
||||
}
|
||||
case *dto.ClaudeRequest:
|
||||
if request != nil && request.Temperature != nil {
|
||||
return *request.Temperature, true
|
||||
}
|
||||
case *dto.GeminiChatRequest:
|
||||
if request != nil && request.GenerationConfig.Temperature != nil {
|
||||
return *request.GenerationConfig.Temperature, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func extractTopP(req dto.Request) (float64, bool) {
|
||||
switch request := req.(type) {
|
||||
case *dto.GeneralOpenAIRequest:
|
||||
if request != nil && request.TopP != nil {
|
||||
return *request.TopP, true
|
||||
}
|
||||
case *dto.OpenAIResponsesRequest:
|
||||
if request != nil && request.TopP != nil {
|
||||
return *request.TopP, true
|
||||
}
|
||||
case *dto.ClaudeRequest:
|
||||
if request != nil && request.TopP != nil {
|
||||
return *request.TopP, true
|
||||
}
|
||||
case *dto.GeminiChatRequest:
|
||||
if request != nil && request.GenerationConfig.TopP != nil {
|
||||
return *request.GenerationConfig.TopP, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func modelModifierDiagnostic(code string, key string, message string) types.ConversionDiagnostic {
|
||||
return types.ConversionDiagnostic{
|
||||
Code: code,
|
||||
Path: "model.@" + key,
|
||||
Message: message,
|
||||
Severity: types.ConversionDiagnosticWarning,
|
||||
}
|
||||
}
|
||||
|
||||
func applyModelControls(req dto.Request, parsed parsedModelModifiers) error {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch request := req.(type) {
|
||||
case *dto.GeneralOpenAIRequest:
|
||||
if parsed.hasTemperature {
|
||||
request.Temperature = parsed.temperature
|
||||
}
|
||||
if parsed.hasTopP {
|
||||
request.TopP = parsed.topP
|
||||
}
|
||||
if parsed.hasThinking {
|
||||
request.ReasoningConversion = reasoning.StateFromIntent(parsed.intent)
|
||||
reasoningConfig := make(map[string]any)
|
||||
if len(request.Reasoning) > 0 {
|
||||
if common.GetJsonType(request.Reasoning) != "object" {
|
||||
return fmt.Errorf("OpenAI reasoning must be a JSON object")
|
||||
}
|
||||
if err := common.Unmarshal(request.Reasoning, &reasoningConfig); err != nil {
|
||||
return fmt.Errorf("invalid OpenAI reasoning config: %w", err)
|
||||
}
|
||||
}
|
||||
if parsed.intent.BudgetTokens != nil {
|
||||
reasoningConfig["enabled"] = parsed.intent.Mode != reasoning.ModeDisabled
|
||||
reasoningConfig["max_tokens"] = *parsed.intent.BudgetTokens
|
||||
delete(reasoningConfig, "effort")
|
||||
request.ReasoningEffort = ""
|
||||
} else {
|
||||
delete(reasoningConfig, "enabled")
|
||||
delete(reasoningConfig, "effort")
|
||||
delete(reasoningConfig, "max_tokens")
|
||||
request.ReasoningEffort = ""
|
||||
if parsed.intent.Effort != "" {
|
||||
request.ReasoningEffort = string(parsed.intent.Effort)
|
||||
}
|
||||
}
|
||||
if len(reasoningConfig) == 0 {
|
||||
request.Reasoning = nil
|
||||
} else {
|
||||
encoded, err := common.Marshal(reasoningConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Reasoning = encoded
|
||||
}
|
||||
}
|
||||
case *dto.OpenAIResponsesRequest:
|
||||
if parsed.hasTemperature {
|
||||
request.Temperature = parsed.temperature
|
||||
}
|
||||
if parsed.hasTopP {
|
||||
request.TopP = parsed.topP
|
||||
}
|
||||
if parsed.hasThinking {
|
||||
request.ReasoningConversion = reasoning.StateFromIntent(parsed.intent)
|
||||
if parsed.intent.Effort != "" {
|
||||
if request.Reasoning == nil {
|
||||
request.Reasoning = &dto.Reasoning{}
|
||||
}
|
||||
request.Reasoning.Effort = string(parsed.intent.Effort)
|
||||
} else if request.Reasoning != nil && parsed.intent.BudgetTokens == nil {
|
||||
request.Reasoning.Effort = ""
|
||||
}
|
||||
}
|
||||
case *dto.ClaudeRequest:
|
||||
if parsed.hasTemperature {
|
||||
request.Temperature = parsed.temperature
|
||||
}
|
||||
if parsed.hasTopP {
|
||||
request.TopP = parsed.topP
|
||||
}
|
||||
if parsed.hasThinking {
|
||||
request.Thinking = nil
|
||||
if len(request.OutputConfig) > 0 && common.GetJsonType(request.OutputConfig) == "object" {
|
||||
var output map[string]any
|
||||
if err := common.Unmarshal(request.OutputConfig, &output); err == nil {
|
||||
delete(output, "effort")
|
||||
encoded, err := common.Marshal(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.OutputConfig = encoded
|
||||
}
|
||||
}
|
||||
}
|
||||
case *dto.GeminiChatRequest:
|
||||
if parsed.hasTemperature {
|
||||
request.GenerationConfig.Temperature = parsed.temperature
|
||||
}
|
||||
if parsed.hasTopP {
|
||||
request.GenerationConfig.TopP = parsed.topP
|
||||
}
|
||||
if parsed.hasThinking {
|
||||
request.GenerationConfig.ThinkingConfig = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user