mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 21:10:51 +08:00
fix(executor): add allowCompat support for reasoning effort normalization
Introduced `allowCompat` parameter to improve compatibility handling for reasoning effort in payloads across OpenAI and similar models.
This commit is contained in:
@@ -54,8 +54,8 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
|
|||||||
from := opts.SourceFormat
|
from := opts.SourceFormat
|
||||||
to := sdktranslator.FromString("codex")
|
to := sdktranslator.FromString("codex")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort", false)
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return resp, errValidate
|
return resp, errValidate
|
||||||
}
|
}
|
||||||
@@ -152,8 +152,8 @@ func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
|
|||||||
to := sdktranslator.FromString("codex")
|
to := sdktranslator.FromString("codex")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
||||||
|
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort", false)
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return nil, errValidate
|
return nil, errValidate
|
||||||
}
|
}
|
||||||
@@ -254,7 +254,7 @@ func (e *CodexExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth
|
|||||||
|
|
||||||
modelForCounting := req.Model
|
modelForCounting := req.Model
|
||||||
|
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning.effort", false)
|
||||||
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
||||||
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
||||||
body, _ = sjson.SetBytes(body, "stream", false)
|
body, _ = sjson.SetBytes(body, "stream", false)
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ func (e *IFlowExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
|
|||||||
from := opts.SourceFormat
|
from := opts.SourceFormat
|
||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort", false)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" {
|
if upstreamModel != "" {
|
||||||
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return resp, errValidate
|
return resp, errValidate
|
||||||
}
|
}
|
||||||
@@ -148,12 +148,12 @@ func (e *IFlowExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
|
|||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
||||||
|
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort", false)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" {
|
if upstreamModel != "" {
|
||||||
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return nil, errValidate
|
return nil, errValidate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,12 +59,13 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
|
|||||||
translated = e.overrideModel(translated, modelOverride)
|
translated = e.overrideModel(translated, modelOverride)
|
||||||
}
|
}
|
||||||
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
||||||
translated = applyReasoningEffortMetadata(translated, req.Metadata, req.Model, "reasoning_effort")
|
allowCompat := e.allowCompatReasoningEffort(req.Model, auth)
|
||||||
|
translated = applyReasoningEffortMetadata(translated, req.Metadata, req.Model, "reasoning_effort", allowCompat)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" && modelOverride == "" {
|
if upstreamModel != "" && modelOverride == "" {
|
||||||
translated, _ = sjson.SetBytes(translated, "model", upstreamModel)
|
translated, _ = sjson.SetBytes(translated, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
translated = normalizeThinkingConfig(translated, upstreamModel)
|
translated = normalizeThinkingConfig(translated, upstreamModel, allowCompat)
|
||||||
if errValidate := validateThinkingConfig(translated, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(translated, upstreamModel); errValidate != nil {
|
||||||
return resp, errValidate
|
return resp, errValidate
|
||||||
}
|
}
|
||||||
@@ -154,12 +155,13 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
|
|||||||
translated = e.overrideModel(translated, modelOverride)
|
translated = e.overrideModel(translated, modelOverride)
|
||||||
}
|
}
|
||||||
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
||||||
translated = applyReasoningEffortMetadata(translated, req.Metadata, req.Model, "reasoning_effort")
|
allowCompat := e.allowCompatReasoningEffort(req.Model, auth)
|
||||||
|
translated = applyReasoningEffortMetadata(translated, req.Metadata, req.Model, "reasoning_effort", allowCompat)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" && modelOverride == "" {
|
if upstreamModel != "" && modelOverride == "" {
|
||||||
translated, _ = sjson.SetBytes(translated, "model", upstreamModel)
|
translated, _ = sjson.SetBytes(translated, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
translated = normalizeThinkingConfig(translated, upstreamModel)
|
translated = normalizeThinkingConfig(translated, upstreamModel, allowCompat)
|
||||||
if errValidate := validateThinkingConfig(translated, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(translated, upstreamModel); errValidate != nil {
|
||||||
return nil, errValidate
|
return nil, errValidate
|
||||||
}
|
}
|
||||||
@@ -325,6 +327,27 @@ func (e *OpenAICompatExecutor) resolveUpstreamModel(alias string, auth *cliproxy
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *OpenAICompatExecutor) allowCompatReasoningEffort(model string, auth *cliproxyauth.Auth) bool {
|
||||||
|
trimmed := strings.TrimSpace(model)
|
||||||
|
if trimmed == "" || e == nil || e.cfg == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
compat := e.resolveCompatConfig(auth)
|
||||||
|
if compat == nil || len(compat.Models) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range compat.Models {
|
||||||
|
entry := compat.Models[i]
|
||||||
|
if strings.EqualFold(strings.TrimSpace(entry.Alias), trimmed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.EqualFold(strings.TrimSpace(entry.Name), trimmed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (e *OpenAICompatExecutor) resolveCompatConfig(auth *cliproxyauth.Auth) *config.OpenAICompatibility {
|
func (e *OpenAICompatExecutor) resolveCompatConfig(auth *cliproxyauth.Auth) *config.OpenAICompatibility {
|
||||||
if auth == nil || e.cfg == nil {
|
if auth == nil || e.cfg == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func applyThinkingMetadataCLI(payload []byte, metadata map[string]any, model str
|
|||||||
// applyReasoningEffortMetadata applies reasoning effort overrides from metadata to the given JSON path.
|
// applyReasoningEffortMetadata applies reasoning effort overrides from metadata to the given JSON path.
|
||||||
// Metadata values take precedence over any existing field when the model supports thinking, intentionally
|
// Metadata values take precedence over any existing field when the model supports thinking, intentionally
|
||||||
// overwriting caller-provided values to honor suffix/default metadata priority.
|
// overwriting caller-provided values to honor suffix/default metadata priority.
|
||||||
func applyReasoningEffortMetadata(payload []byte, metadata map[string]any, model, field string) []byte {
|
func applyReasoningEffortMetadata(payload []byte, metadata map[string]any, model, field string, allowCompat bool) []byte {
|
||||||
if len(metadata) == 0 {
|
if len(metadata) == 0 {
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
@@ -59,20 +59,20 @@ func applyReasoningEffortMetadata(payload []byte, metadata map[string]any, model
|
|||||||
if baseModel == "" {
|
if baseModel == "" {
|
||||||
baseModel = model
|
baseModel = model
|
||||||
}
|
}
|
||||||
if !util.ModelSupportsThinking(baseModel) && !util.IsOpenAICompatibilityModel(baseModel) {
|
if !util.ModelSupportsThinking(baseModel) && !allowCompat {
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
if effort, ok := util.ReasoningEffortFromMetadata(metadata); ok && effort != "" {
|
if effort, ok := util.ReasoningEffortFromMetadata(metadata); ok && effort != "" {
|
||||||
if util.ModelUsesThinkingLevels(model) {
|
if util.ModelUsesThinkingLevels(baseModel) || allowCompat {
|
||||||
if updated, err := sjson.SetBytes(payload, field, effort); err == nil {
|
if updated, err := sjson.SetBytes(payload, field, effort); err == nil {
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fallback: numeric thinking_budget suffix for level-based (OpenAI-style) models.
|
// Fallback: numeric thinking_budget suffix for level-based (OpenAI-style) models.
|
||||||
if util.ModelUsesThinkingLevels(model) {
|
if util.ModelUsesThinkingLevels(baseModel) || allowCompat {
|
||||||
if budget, _, _, matched := util.ThinkingFromMetadata(metadata); matched && budget != nil {
|
if budget, _, _, matched := util.ThinkingFromMetadata(metadata); matched && budget != nil {
|
||||||
if effort, ok := util.OpenAIThinkingBudgetToEffort(model, *budget); ok && effort != "" {
|
if effort, ok := util.OpenAIThinkingBudgetToEffort(baseModel, *budget); ok && effort != "" {
|
||||||
if updated, err := sjson.SetBytes(payload, field, effort); err == nil {
|
if updated, err := sjson.SetBytes(payload, field, effort); err == nil {
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
@@ -237,13 +237,13 @@ func matchModelPattern(pattern, model string) bool {
|
|||||||
// reasoning fields. For models with level-based thinking, it validates and
|
// reasoning fields. For models with level-based thinking, it validates and
|
||||||
// normalizes the reasoning effort level. For models with numeric budget thinking,
|
// normalizes the reasoning effort level. For models with numeric budget thinking,
|
||||||
// it strips the effort string fields.
|
// it strips the effort string fields.
|
||||||
func normalizeThinkingConfig(payload []byte, model string) []byte {
|
func normalizeThinkingConfig(payload []byte, model string, allowCompat bool) []byte {
|
||||||
if len(payload) == 0 || model == "" {
|
if len(payload) == 0 || model == "" {
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ModelSupportsThinking(model) {
|
if !util.ModelSupportsThinking(model) {
|
||||||
if util.IsOpenAICompatibilityModel(model) {
|
if allowCompat {
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
return stripThinkingFields(payload, false)
|
return stripThinkingFields(payload, false)
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ func (e *QwenExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
|
|||||||
from := opts.SourceFormat
|
from := opts.SourceFormat
|
||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort", false)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" {
|
if upstreamModel != "" {
|
||||||
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return resp, errValidate
|
return resp, errValidate
|
||||||
}
|
}
|
||||||
@@ -131,12 +131,12 @@ func (e *QwenExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut
|
|||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true)
|
||||||
|
|
||||||
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort")
|
body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort", false)
|
||||||
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata)
|
||||||
if upstreamModel != "" {
|
if upstreamModel != "" {
|
||||||
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
body, _ = sjson.SetBytes(body, "model", upstreamModel)
|
||||||
}
|
}
|
||||||
body = normalizeThinkingConfig(body, upstreamModel)
|
body = normalizeThinkingConfig(body, upstreamModel, false)
|
||||||
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil {
|
||||||
return nil, errValidate
|
return nil, errValidate
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user