feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
|
||||
"github.com/QuantumNous/new-api/relaykit/types"
|
||||
)
|
||||
|
||||
type Effort string
|
||||
@@ -133,32 +134,62 @@ func ParseEffort(value string) (Effort, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeIntent(intent Intent) (Intent, error) {
|
||||
// reasoningDiagnostic describes a best-effort resolution between reasoning
|
||||
// controls that is not tied to a single provider's field layout. The target
|
||||
// format is filled in by the conversion pipeline.
|
||||
func reasoningDiagnostic(code string, message string) types.ConversionDiagnostic {
|
||||
return types.ConversionDiagnostic{
|
||||
Code: code,
|
||||
Path: "reasoning",
|
||||
Message: message,
|
||||
Severity: types.ConversionDiagnosticWarning,
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeIntent canonicalizes one intent. Only unknown effort or mode values
|
||||
// are errors; contradictions between mode, effort, and budget are resolved in
|
||||
// favor of the more specific control (a numeric budget beats a mode or effort,
|
||||
// effort none beats an enabling mode) and reported as diagnostics.
|
||||
func normalizeIntent(intent Intent) (Intent, []types.ConversionDiagnostic, error) {
|
||||
effort, err := ParseEffort(string(intent.Effort))
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent.Effort = effort
|
||||
|
||||
switch intent.Mode {
|
||||
case ModeUnset, ModeEnabled, ModeAdaptive, ModeDisabled:
|
||||
default:
|
||||
return Intent{}, fmt.Errorf("unsupported reasoning mode %q", intent.Mode)
|
||||
return Intent{}, nil, fmt.Errorf("unsupported reasoning mode %q", intent.Mode)
|
||||
}
|
||||
|
||||
var diagnostics []types.ConversionDiagnostic
|
||||
if intent.BudgetTokens != nil {
|
||||
budget := *intent.BudgetTokens
|
||||
if budget < -1 {
|
||||
return Intent{}, fmt.Errorf("thinking budget must be -1 or non-negative, got %d", budget)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"reasoning_budget_adjusted",
|
||||
fmt.Sprintf("thinking budget %d is below -1; using the dynamic budget -1", budget),
|
||||
))
|
||||
budget = -1
|
||||
intent.BudgetTokens = &budget
|
||||
}
|
||||
if budget == 0 {
|
||||
if intent.Mode == ModeEnabled || intent.Mode == ModeAdaptive || (intent.Effort != "" && intent.Effort != EffortNone) {
|
||||
return Intent{}, fmt.Errorf("%w: zero budget disables thinking", ErrEffortConflict)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
"a zero thinking budget disables thinking; the enabling mode or effort was ignored",
|
||||
))
|
||||
}
|
||||
intent.Mode = ModeDisabled
|
||||
intent.Effort = EffortNone
|
||||
} else if intent.Mode == ModeDisabled || intent.Effort == EffortNone {
|
||||
return Intent{}, fmt.Errorf("%w: a non-zero budget enables thinking", ErrEffortConflict)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
fmt.Sprintf("a non-zero thinking budget %d enables thinking; the disabling mode or effort was ignored", budget),
|
||||
))
|
||||
intent.Mode = ModeEnabled
|
||||
intent.Effort = ""
|
||||
} else if intent.Mode == ModeUnset {
|
||||
intent.Mode = ModeEnabled
|
||||
}
|
||||
@@ -166,108 +197,137 @@ func normalizeIntent(intent Intent) (Intent, error) {
|
||||
|
||||
if intent.Effort == EffortNone {
|
||||
if intent.Mode == ModeEnabled || intent.Mode == ModeAdaptive {
|
||||
return Intent{}, fmt.Errorf("%w: effort none disables thinking", ErrEffortConflict)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
"effort none disables thinking; the enabling mode was ignored",
|
||||
))
|
||||
}
|
||||
intent.Mode = ModeDisabled
|
||||
}
|
||||
|
||||
return intent, nil
|
||||
return intent, diagnostics, nil
|
||||
}
|
||||
|
||||
// MergeExplicitAndSuffix combines structured request fields with a model-name
|
||||
// alias. Contradictions are rejected because the alias may carry a distinct
|
||||
// billing identity; silently choosing either side would make request semantics
|
||||
// and accounting disagree.
|
||||
func MergeExplicitAndSuffix(explicit Intent, suffix Intent, model string) (Intent, error) {
|
||||
var err error
|
||||
explicit, err = normalizeIntent(explicit)
|
||||
// alias or @ modifier. The model name wins wherever the two disagree: it is
|
||||
// also the billing identity, so upstream behavior and accounting must follow
|
||||
// it. The alias only overrides what it states explicitly; a bare enable such
|
||||
// as -thinking keeps the strength supplied by request fields. Every override
|
||||
// is reported as a suffix_overrode_request diagnostic.
|
||||
func MergeExplicitAndSuffix(explicit Intent, suffix Intent, model string) (Intent, []types.ConversionDiagnostic, error) {
|
||||
explicit, diagnostics, err := normalizeIntent(explicit)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
suffix, err = normalizeIntent(suffix)
|
||||
suffix, suffixDiagnostics, err := normalizeIntent(suffix)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
diagnostics = append(diagnostics, suffixDiagnostics...)
|
||||
|
||||
if !explicit.HasStrength() {
|
||||
if explicit.IncludeThoughts != nil {
|
||||
suffix.IncludeThoughts = explicit.IncludeThoughts
|
||||
}
|
||||
return suffix, nil
|
||||
return suffix, diagnostics, nil
|
||||
}
|
||||
if !suffix.HasStrength() {
|
||||
if explicit.IncludeThoughts == nil {
|
||||
explicit.IncludeThoughts = suffix.IncludeThoughts
|
||||
}
|
||||
return explicit, nil
|
||||
}
|
||||
|
||||
explicitDisabled := explicit.Mode == ModeDisabled || explicit.Effort == EffortNone
|
||||
suffixDisabled := suffix.Mode == ModeDisabled || suffix.Effort == EffortNone
|
||||
if explicitDisabled != suffixDisabled {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit fields and model suffix disagree about whether thinking is enabled", ErrEffortConflict, model)
|
||||
}
|
||||
if !explicitDisabled && explicit.Effort != "" && suffix.Effort != "" && explicit.Effort != suffix.Effort {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit effort %q differs from suffix effort %q", ErrEffortConflict, model, explicit.Effort, suffix.Effort)
|
||||
}
|
||||
if explicit.BudgetTokens != nil && suffix.BudgetTokens != nil && *explicit.BudgetTokens != *suffix.BudgetTokens {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit budget %d differs from suffix budget %d", ErrEffortConflict, model, *explicit.BudgetTokens, *suffix.BudgetTokens)
|
||||
}
|
||||
if (explicit.Effort != "" && explicit.Effort != EffortNone && suffix.BudgetTokens != nil) ||
|
||||
(explicit.BudgetTokens != nil && suffix.Effort != "" && suffix.Effort != EffortNone) {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: effort and an exact suffix budget cannot both select reasoning strength", ErrEffortConflict, model)
|
||||
return explicit, diagnostics, nil
|
||||
}
|
||||
|
||||
merged := suffix
|
||||
if explicit.Mode != ModeUnset {
|
||||
merged.Mode = explicit.Mode
|
||||
}
|
||||
if explicit.Effort != "" {
|
||||
merged.Effort = explicit.Effort
|
||||
}
|
||||
if explicit.BudgetTokens != nil {
|
||||
merged.BudgetTokens = explicit.BudgetTokens
|
||||
merged.BudgetSource = explicit.BudgetSource
|
||||
}
|
||||
if explicit.IncludeThoughts != nil {
|
||||
merged.IncludeThoughts = explicit.IncludeThoughts
|
||||
}
|
||||
return normalizeIntent(merged)
|
||||
explicitDisabled := explicit.Mode == ModeDisabled || explicit.Effort == EffortNone
|
||||
suffixDisabled := suffix.Mode == ModeDisabled || suffix.Effort == EffortNone
|
||||
if explicitDisabled != suffixDisabled {
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"suffix_overrode_request",
|
||||
fmt.Sprintf("model %q: the model name and request fields disagree about whether thinking is enabled; the model name wins", model),
|
||||
))
|
||||
return merged, diagnostics, nil
|
||||
}
|
||||
if suffixDisabled {
|
||||
return merged, diagnostics, nil
|
||||
}
|
||||
|
||||
if merged.Mode == ModeEnabled && explicit.Mode == ModeAdaptive {
|
||||
merged.Mode = ModeAdaptive
|
||||
}
|
||||
if suffix.Effort == "" && suffix.BudgetTokens == nil {
|
||||
// A bare enable says nothing about strength; request fields supply it.
|
||||
merged.Effort = explicit.Effort
|
||||
merged.BudgetTokens = explicit.BudgetTokens
|
||||
merged.BudgetSource = explicit.BudgetSource
|
||||
return merged, diagnostics, nil
|
||||
}
|
||||
if explicit.Effort != "" && explicit.Effort != suffix.Effort {
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"suffix_overrode_request",
|
||||
fmt.Sprintf("model %q: request effort %q was replaced by the reasoning strength in the model name", model, explicit.Effort),
|
||||
))
|
||||
}
|
||||
if explicit.BudgetTokens != nil && (suffix.BudgetTokens == nil || *explicit.BudgetTokens != *suffix.BudgetTokens) {
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"suffix_overrode_request",
|
||||
fmt.Sprintf("model %q: request thinking budget %d was replaced by the reasoning strength in the model name", model, *explicit.BudgetTokens),
|
||||
))
|
||||
}
|
||||
return merged, diagnostics, nil
|
||||
}
|
||||
|
||||
// MergeExplicit combines two structured representations of the same request.
|
||||
// A numeric budget and an effort may coexist: Claude and OpenRouter expose both
|
||||
// controls, and keeping both is what lets an in-memory OpenAI pivot preserve an
|
||||
// exact budget for budget-based targets while retaining an effort for
|
||||
// level-based targets.
|
||||
func MergeExplicit(primary Intent, secondary Intent, model string) (Intent, error) {
|
||||
var err error
|
||||
primary, err = normalizeIntent(primary)
|
||||
// primary wins wherever they disagree; callers pass the more specific
|
||||
// representation first. A numeric budget and an effort may coexist: Claude and
|
||||
// OpenRouter expose both controls, and keeping both is what lets an in-memory
|
||||
// OpenAI pivot preserve an exact budget for budget-based targets while
|
||||
// retaining an effort for level-based targets. Disagreements are reported as
|
||||
// explicit_fields_conflict diagnostics.
|
||||
func MergeExplicit(primary Intent, secondary Intent, model string) (Intent, []types.ConversionDiagnostic, error) {
|
||||
primary, diagnostics, err := normalizeIntent(primary)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
secondary, err = normalizeIntent(secondary)
|
||||
secondary, secondaryDiagnostics, err := normalizeIntent(secondary)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
diagnostics = append(diagnostics, secondaryDiagnostics...)
|
||||
|
||||
if primary.IsEmpty() {
|
||||
return secondary, nil
|
||||
return secondary, diagnostics, nil
|
||||
}
|
||||
if secondary.IsEmpty() {
|
||||
return primary, nil
|
||||
return primary, diagnostics, nil
|
||||
}
|
||||
|
||||
primaryDisabled := primary.Mode == ModeDisabled || primary.Effort == EffortNone
|
||||
secondaryDisabled := secondary.Mode == ModeDisabled || secondary.Effort == EffortNone
|
||||
if primary.HasStrength() && secondary.HasStrength() && primaryDisabled != secondaryDisabled {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit fields disagree about whether thinking is enabled", ErrEffortConflict, model)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
fmt.Sprintf("model %q: reasoning fields disagree about whether thinking is enabled; keeping the more specific field", model),
|
||||
))
|
||||
if primary.IncludeThoughts == nil {
|
||||
primary.IncludeThoughts = secondary.IncludeThoughts
|
||||
}
|
||||
return primary, diagnostics, nil
|
||||
}
|
||||
if primary.Effort != "" && secondary.Effort != "" && primary.Effort != secondary.Effort {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit efforts %q and %q differ", ErrEffortConflict, model, primary.Effort, secondary.Effort)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
fmt.Sprintf("model %q: reasoning efforts %q and %q differ; keeping %q", model, primary.Effort, secondary.Effort, primary.Effort),
|
||||
))
|
||||
}
|
||||
if primary.BudgetTokens != nil && secondary.BudgetTokens != nil && *primary.BudgetTokens != *secondary.BudgetTokens {
|
||||
return Intent{}, fmt.Errorf("%w for model %q: explicit budgets %d and %d differ", ErrEffortConflict, model, *primary.BudgetTokens, *secondary.BudgetTokens)
|
||||
diagnostics = append(diagnostics, reasoningDiagnostic(
|
||||
"explicit_fields_conflict",
|
||||
fmt.Sprintf("model %q: thinking budgets %d and %d differ; keeping %d", model, *primary.BudgetTokens, *secondary.BudgetTokens, *primary.BudgetTokens),
|
||||
))
|
||||
}
|
||||
|
||||
merged := secondary
|
||||
@@ -284,7 +344,11 @@ func MergeExplicit(primary Intent, secondary Intent, model string) (Intent, erro
|
||||
if primary.IncludeThoughts != nil {
|
||||
merged.IncludeThoughts = primary.IncludeThoughts
|
||||
}
|
||||
return normalizeIntent(merged)
|
||||
merged, mergedDiagnostics, err := normalizeIntent(merged)
|
||||
if err != nil {
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
return merged, append(diagnostics, mergedDiagnostics...), nil
|
||||
}
|
||||
|
||||
type openRouterReasoning struct {
|
||||
@@ -294,9 +358,9 @@ type openRouterReasoning struct {
|
||||
Exclude *bool `json:"exclude,omitempty"`
|
||||
}
|
||||
|
||||
func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, []types.ConversionDiagnostic, error) {
|
||||
if req == nil {
|
||||
return Intent{}, nil
|
||||
return Intent{}, nil, nil
|
||||
}
|
||||
|
||||
var intent Intent
|
||||
@@ -304,7 +368,7 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
if req.ReasoningEffort != "" {
|
||||
effort, err := ParseEffort(req.ReasoningEffort)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent.Effort = effort
|
||||
if effort == EffortNone {
|
||||
@@ -314,10 +378,11 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var diagnostics []types.ConversionDiagnostic
|
||||
if len(req.Reasoning) > 0 {
|
||||
var raw openRouterReasoning
|
||||
if err := kitutil.Unmarshal(req.Reasoning, &raw); err != nil {
|
||||
return Intent{}, fmt.Errorf("invalid reasoning config: %w", err)
|
||||
return Intent{}, nil, fmt.Errorf("invalid reasoning config: %w", err)
|
||||
}
|
||||
nested := Intent{BudgetTokens: raw.MaxTokens, Source: SourceExplicit, BudgetSource: SourceExplicit}
|
||||
if raw.Enabled != nil {
|
||||
@@ -331,7 +396,7 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
if raw.Effort != "" {
|
||||
effort, err := ParseEffort(raw.Effort)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
nested.Effort = effort
|
||||
if effort == EffortNone {
|
||||
@@ -344,15 +409,20 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
include := !*raw.Exclude
|
||||
nested.IncludeThoughts = &include
|
||||
}
|
||||
var err error
|
||||
intent, err = MergeExplicit(intent, nested, req.Model)
|
||||
merged, mergeDiagnostics, err := MergeExplicit(intent, nested, req.Model)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent = merged
|
||||
diagnostics = append(diagnostics, mergeDiagnostics...)
|
||||
}
|
||||
|
||||
if req.ReasoningConversion == nil {
|
||||
return normalizeIntent(intent)
|
||||
normalized, normalizeDiagnostics, err := normalizeIntent(intent)
|
||||
if err != nil {
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
return normalized, append(diagnostics, normalizeDiagnostics...), nil
|
||||
}
|
||||
pivot := Intent{
|
||||
Mode: Mode(req.ReasoningConversion.Mode),
|
||||
@@ -369,7 +439,11 @@ func FromOpenAIChat(req *dto.GeneralOpenAIRequest) (Intent, error) {
|
||||
intent.Mode = ModeUnset
|
||||
}
|
||||
}
|
||||
return MergeExplicit(intent, pivot, req.Model)
|
||||
merged, mergeDiagnostics, err := MergeExplicit(intent, pivot, req.Model)
|
||||
if err != nil {
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
return merged, append(diagnostics, mergeDiagnostics...), nil
|
||||
}
|
||||
|
||||
// ApplyToOpenAIChat writes the portable portion of an intent to the OpenAI
|
||||
@@ -379,7 +453,7 @@ func ApplyToOpenAIChat(req *dto.GeneralOpenAIRequest, intent Intent) error {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
intent, err := normalizeIntent(intent)
|
||||
intent, _, err := normalizeIntent(intent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -407,7 +481,7 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
intent, err := normalizeIntent(intent)
|
||||
intent, _, err := normalizeIntent(intent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -436,9 +510,9 @@ func ApplyToOpenAIResponses(req *dto.OpenAIResponsesRequest, intent Intent) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, error) {
|
||||
func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, []types.ConversionDiagnostic, error) {
|
||||
if req == nil {
|
||||
return Intent{}, nil
|
||||
return Intent{}, nil, nil
|
||||
}
|
||||
var intent Intent
|
||||
if req.Reasoning != nil {
|
||||
@@ -446,7 +520,7 @@ func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, error) {
|
||||
if req.Reasoning.Effort != "" {
|
||||
effort, err := ParseEffort(req.Reasoning.Effort)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent.Effort = effort
|
||||
intent.Mode = ModeEnabled
|
||||
@@ -480,11 +554,16 @@ func FromOpenAIResponses(req *dto.OpenAIResponsesRequest) (Intent, error) {
|
||||
return MergeExplicit(intent, pivot, req.Model)
|
||||
}
|
||||
|
||||
func FromClaude(req *dto.ClaudeRequest) (Intent, error) {
|
||||
// FromClaude reads Claude's native thinking and output_config controls. Budget
|
||||
// bounds are not enforced here: the Claude renderer clamps budgets into
|
||||
// 1024 <= budget_tokens < max_tokens and reports the adjustment, and other
|
||||
// targets map the raw budget to an effort.
|
||||
func FromClaude(req *dto.ClaudeRequest) (Intent, []types.ConversionDiagnostic, error) {
|
||||
if req == nil {
|
||||
return Intent{}, nil
|
||||
return Intent{}, nil, nil
|
||||
}
|
||||
var intent Intent
|
||||
var diagnostics []types.ConversionDiagnostic
|
||||
intent.Source = SourceNative
|
||||
if req.Thinking != nil {
|
||||
switch req.Thinking.Type {
|
||||
@@ -496,17 +575,16 @@ func FromClaude(req *dto.ClaudeRequest) (Intent, error) {
|
||||
intent.Mode = ModeDisabled
|
||||
intent.Effort = EffortNone
|
||||
default:
|
||||
return Intent{}, fmt.Errorf("unsupported Claude thinking type %q", req.Thinking.Type)
|
||||
diagnostics = append(diagnostics, types.ConversionDiagnostic{
|
||||
Code: "claude_thinking_type_coerced",
|
||||
Path: "thinking",
|
||||
Message: fmt.Sprintf("unsupported Claude thinking type %q was treated as enabled", req.Thinking.Type),
|
||||
Severity: types.ConversionDiagnosticWarning,
|
||||
})
|
||||
intent.Mode = ModeEnabled
|
||||
}
|
||||
intent.BudgetTokens = req.Thinking.BudgetTokens
|
||||
if req.Thinking.BudgetTokens != nil {
|
||||
budget := *req.Thinking.BudgetTokens
|
||||
if budget < 1024 {
|
||||
return Intent{}, fmt.Errorf("Claude thinking budget_tokens must be at least 1024, got %d", budget)
|
||||
}
|
||||
if req.MaxTokens != nil && uint(budget) >= *req.MaxTokens {
|
||||
return Intent{}, fmt.Errorf("Claude thinking budget_tokens must be less than max_tokens")
|
||||
}
|
||||
intent.BudgetSource = SourceNative
|
||||
}
|
||||
switch req.Thinking.Display {
|
||||
@@ -521,29 +599,37 @@ func FromClaude(req *dto.ClaudeRequest) (Intent, error) {
|
||||
if len(req.OutputConfig) > 0 {
|
||||
var output dto.OutputConfigForEffort
|
||||
if err := kitutil.Unmarshal(req.OutputConfig, &output); err != nil {
|
||||
return Intent{}, fmt.Errorf("invalid Claude output_config: %w", err)
|
||||
return Intent{}, nil, fmt.Errorf("invalid Claude output_config: %w", err)
|
||||
}
|
||||
if output.Effort != "" {
|
||||
effort, err := ParseEffort(output.Effort)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent.Effort = effort
|
||||
}
|
||||
}
|
||||
if intent.Mode == ModeDisabled && intent.Effort != "" && intent.Effort != EffortNone {
|
||||
return intent, nil
|
||||
return intent, diagnostics, nil
|
||||
}
|
||||
return normalizeIntent(intent)
|
||||
normalized, normalizeDiagnostics, err := normalizeIntent(intent)
|
||||
if err != nil {
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
return normalized, append(diagnostics, normalizeDiagnostics...), nil
|
||||
}
|
||||
|
||||
func FromGemini(req *dto.GeminiChatRequest) (Intent, error) {
|
||||
// FromGemini reads a native thinkingConfig. A config carrying both a budget
|
||||
// and a level is rejected here because the intent alone cannot tell which one
|
||||
// the target model accepts; NormalizeGeminiThinkingConfig resolves that pair
|
||||
// first whenever the model's dialect is known.
|
||||
func FromGemini(req *dto.GeminiChatRequest) (Intent, []types.ConversionDiagnostic, error) {
|
||||
if req == nil || req.GenerationConfig.ThinkingConfig == nil {
|
||||
return Intent{}, nil
|
||||
return Intent{}, nil, nil
|
||||
}
|
||||
config := req.GenerationConfig.ThinkingConfig
|
||||
if config.ThinkingBudget != nil && config.ThinkingLevel != "" {
|
||||
return Intent{}, fmt.Errorf("%w: Gemini thinkingBudget and thinkingLevel cannot both be set", ErrEffortConflict)
|
||||
return Intent{}, nil, fmt.Errorf("%w: Gemini thinkingBudget and thinkingLevel cannot both be set", ErrEffortConflict)
|
||||
}
|
||||
intent := Intent{
|
||||
BudgetTokens: config.ThinkingBudget,
|
||||
@@ -554,16 +640,19 @@ func FromGemini(req *dto.GeminiChatRequest) (Intent, error) {
|
||||
if config.ThinkingLevel != "" {
|
||||
effort, err := ParseEffort(config.ThinkingLevel)
|
||||
if err != nil {
|
||||
return Intent{}, err
|
||||
return Intent{}, nil, err
|
||||
}
|
||||
intent.Effort = effort
|
||||
intent.Mode = ModeEnabled
|
||||
if effort == EffortNone {
|
||||
intent.Mode = ModeDisabled
|
||||
}
|
||||
}
|
||||
return normalizeIntent(intent)
|
||||
}
|
||||
|
||||
func EffectiveEffort(intent Intent) Effort {
|
||||
intent, err := normalizeIntent(intent)
|
||||
intent, _, err := normalizeIntent(intent)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user