refactor(chatprovider): move fallback option merging out of chatd

This commit is contained in:
Kyle Carberry
2026-02-24 15:43:39 -05:00
parent 18ba33415e
commit 525a379088
3 changed files with 442 additions and 348 deletions
+1 -348
View File
@@ -1398,360 +1398,13 @@ func applyFallbackChatModelConfig(
return config
}
mergeMissingChatModelCallConfig(
chatprovider.MergeMissingCallConfig(
&config.ChatModelCallConfig,
defaults.ChatModelCallConfig,
)
return config
}
func mergeMissingChatModelCallConfig(
dst *codersdk.ChatModelCallConfig,
defaults codersdk.ChatModelCallConfig,
) {
if dst.MaxOutputTokens == nil {
dst.MaxOutputTokens = defaults.MaxOutputTokens
}
if dst.Temperature == nil {
dst.Temperature = defaults.Temperature
}
if dst.TopP == nil {
dst.TopP = defaults.TopP
}
if dst.TopK == nil {
dst.TopK = defaults.TopK
}
if dst.PresencePenalty == nil {
dst.PresencePenalty = defaults.PresencePenalty
}
if dst.FrequencyPenalty == nil {
dst.FrequencyPenalty = defaults.FrequencyPenalty
}
mergeMissingChatModelProviderOptions(&dst.ProviderOptions, defaults.ProviderOptions)
}
func mergeMissingChatModelProviderOptions(
dst **codersdk.ChatModelProviderOptions,
defaults *codersdk.ChatModelProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
mergeMissingOpenAIProviderOptions(&(*dst).OpenAI, defaults.OpenAI)
mergeMissingAnthropicProviderOptions(&(*dst).Anthropic, defaults.Anthropic)
mergeMissingGoogleProviderOptions(&(*dst).Google, defaults.Google)
mergeMissingOpenAICompatProviderOptions(&(*dst).OpenAICompat, defaults.OpenAICompat)
mergeMissingOpenRouterProviderOptions(&(*dst).OpenRouter, defaults.OpenRouter)
mergeMissingVercelProviderOptions(&(*dst).Vercel, defaults.Vercel)
}
func mergeMissingOpenAIProviderOptions(
dst **codersdk.ChatModelOpenAIProviderOptions,
defaults *codersdk.ChatModelOpenAIProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.Include == nil {
current.Include = defaults.Include
}
if current.Instructions == nil {
current.Instructions = defaults.Instructions
}
if current.LogitBias == nil {
current.LogitBias = defaults.LogitBias
}
if current.LogProbs == nil {
current.LogProbs = defaults.LogProbs
}
if current.TopLogProbs == nil {
current.TopLogProbs = defaults.TopLogProbs
}
if current.MaxToolCalls == nil {
current.MaxToolCalls = defaults.MaxToolCalls
}
if current.ParallelToolCalls == nil {
current.ParallelToolCalls = defaults.ParallelToolCalls
}
if current.User == nil {
current.User = defaults.User
}
if current.ReasoningEffort == nil {
current.ReasoningEffort = defaults.ReasoningEffort
}
if current.ReasoningSummary == nil {
current.ReasoningSummary = defaults.ReasoningSummary
}
if current.MaxCompletionTokens == nil {
current.MaxCompletionTokens = defaults.MaxCompletionTokens
}
if current.TextVerbosity == nil {
current.TextVerbosity = defaults.TextVerbosity
}
if current.Prediction == nil {
current.Prediction = defaults.Prediction
}
if current.Store == nil {
current.Store = defaults.Store
}
if current.Metadata == nil {
current.Metadata = defaults.Metadata
}
if current.PromptCacheKey == nil {
current.PromptCacheKey = defaults.PromptCacheKey
}
if current.SafetyIdentifier == nil {
current.SafetyIdentifier = defaults.SafetyIdentifier
}
if current.ServiceTier == nil {
current.ServiceTier = defaults.ServiceTier
}
if current.StructuredOutputs == nil {
current.StructuredOutputs = defaults.StructuredOutputs
}
if current.StrictJSONSchema == nil {
current.StrictJSONSchema = defaults.StrictJSONSchema
}
}
func mergeMissingAnthropicProviderOptions(
dst **codersdk.ChatModelAnthropicProviderOptions,
defaults *codersdk.ChatModelAnthropicProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.SendReasoning == nil {
current.SendReasoning = defaults.SendReasoning
}
if current.Thinking == nil {
current.Thinking = defaults.Thinking
} else if defaults.Thinking != nil && current.Thinking.BudgetTokens == nil {
current.Thinking.BudgetTokens = defaults.Thinking.BudgetTokens
}
if current.Effort == nil {
current.Effort = defaults.Effort
}
if current.DisableParallelToolUse == nil {
current.DisableParallelToolUse = defaults.DisableParallelToolUse
}
}
func mergeMissingGoogleProviderOptions(
dst **codersdk.ChatModelGoogleProviderOptions,
defaults *codersdk.ChatModelGoogleProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.ThinkingConfig == nil {
current.ThinkingConfig = defaults.ThinkingConfig
} else if defaults.ThinkingConfig != nil {
if current.ThinkingConfig.ThinkingBudget == nil {
current.ThinkingConfig.ThinkingBudget = defaults.ThinkingConfig.ThinkingBudget
}
if current.ThinkingConfig.IncludeThoughts == nil {
current.ThinkingConfig.IncludeThoughts = defaults.ThinkingConfig.IncludeThoughts
}
}
if strings.TrimSpace(current.CachedContent) == "" {
current.CachedContent = defaults.CachedContent
}
if current.SafetySettings == nil {
current.SafetySettings = defaults.SafetySettings
}
if strings.TrimSpace(current.Threshold) == "" {
current.Threshold = defaults.Threshold
}
}
func mergeMissingOpenAICompatProviderOptions(
dst **codersdk.ChatModelOpenAICompatProviderOptions,
defaults *codersdk.ChatModelOpenAICompatProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.User == nil {
current.User = defaults.User
}
if current.ReasoningEffort == nil {
current.ReasoningEffort = defaults.ReasoningEffort
}
}
func mergeMissingOpenRouterProviderOptions(
dst **codersdk.ChatModelOpenRouterProviderOptions,
defaults *codersdk.ChatModelOpenRouterProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.Reasoning == nil {
current.Reasoning = defaults.Reasoning
} else if defaults.Reasoning != nil {
if current.Reasoning.Enabled == nil {
current.Reasoning.Enabled = defaults.Reasoning.Enabled
}
if current.Reasoning.Exclude == nil {
current.Reasoning.Exclude = defaults.Reasoning.Exclude
}
if current.Reasoning.MaxTokens == nil {
current.Reasoning.MaxTokens = defaults.Reasoning.MaxTokens
}
if current.Reasoning.Effort == nil {
current.Reasoning.Effort = defaults.Reasoning.Effort
}
}
if current.ExtraBody == nil {
current.ExtraBody = defaults.ExtraBody
}
if current.IncludeUsage == nil {
current.IncludeUsage = defaults.IncludeUsage
}
if current.LogitBias == nil {
current.LogitBias = defaults.LogitBias
}
if current.LogProbs == nil {
current.LogProbs = defaults.LogProbs
}
if current.ParallelToolCalls == nil {
current.ParallelToolCalls = defaults.ParallelToolCalls
}
if current.User == nil {
current.User = defaults.User
}
if current.Provider == nil {
current.Provider = defaults.Provider
} else if defaults.Provider != nil {
if current.Provider.Order == nil {
current.Provider.Order = defaults.Provider.Order
}
if current.Provider.AllowFallbacks == nil {
current.Provider.AllowFallbacks = defaults.Provider.AllowFallbacks
}
if current.Provider.RequireParameters == nil {
current.Provider.RequireParameters = defaults.Provider.RequireParameters
}
if current.Provider.DataCollection == nil {
current.Provider.DataCollection = defaults.Provider.DataCollection
}
if current.Provider.Only == nil {
current.Provider.Only = defaults.Provider.Only
}
if current.Provider.Ignore == nil {
current.Provider.Ignore = defaults.Provider.Ignore
}
if current.Provider.Quantizations == nil {
current.Provider.Quantizations = defaults.Provider.Quantizations
}
if current.Provider.Sort == nil {
current.Provider.Sort = defaults.Provider.Sort
}
}
}
func mergeMissingVercelProviderOptions(
dst **codersdk.ChatModelVercelProviderOptions,
defaults *codersdk.ChatModelVercelProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
if current.Reasoning == nil {
current.Reasoning = defaults.Reasoning
} else if defaults.Reasoning != nil {
if current.Reasoning.Enabled == nil {
current.Reasoning.Enabled = defaults.Reasoning.Enabled
}
if current.Reasoning.MaxTokens == nil {
current.Reasoning.MaxTokens = defaults.Reasoning.MaxTokens
}
if current.Reasoning.Effort == nil {
current.Reasoning.Effort = defaults.Reasoning.Effort
}
if current.Reasoning.Exclude == nil {
current.Reasoning.Exclude = defaults.Reasoning.Exclude
}
}
if current.ProviderOptions == nil {
current.ProviderOptions = defaults.ProviderOptions
} else if defaults.ProviderOptions != nil {
if current.ProviderOptions.Order == nil {
current.ProviderOptions.Order = defaults.ProviderOptions.Order
}
if current.ProviderOptions.Models == nil {
current.ProviderOptions.Models = defaults.ProviderOptions.Models
}
}
if current.User == nil {
current.User = defaults.User
}
if current.LogitBias == nil {
current.LogitBias = defaults.LogitBias
}
if current.LogProbs == nil {
current.LogProbs = defaults.LogProbs
}
if current.TopLogProbs == nil {
current.TopLogProbs = defaults.TopLogProbs
}
if current.ParallelToolCalls == nil {
current.ParallelToolCalls = defaults.ParallelToolCalls
}
if current.ExtraBody == nil {
current.ExtraBody = defaults.ExtraBody
}
}
func (p *Processor) resolveFallbackChatModelConfig(
ctx context.Context,
providerKeys ProviderAPIKeys,
+331
View File
@@ -557,6 +557,337 @@ func normalizedEnumValue(value string, allowed ...string) *string {
return nil
}
// MergeMissingCallConfig fills unset call config values from defaults.
func MergeMissingCallConfig(
dst *codersdk.ChatModelCallConfig,
defaults codersdk.ChatModelCallConfig,
) {
if dst.MaxOutputTokens == nil {
dst.MaxOutputTokens = defaults.MaxOutputTokens
}
if dst.Temperature == nil {
dst.Temperature = defaults.Temperature
}
if dst.TopP == nil {
dst.TopP = defaults.TopP
}
if dst.TopK == nil {
dst.TopK = defaults.TopK
}
if dst.PresencePenalty == nil {
dst.PresencePenalty = defaults.PresencePenalty
}
if dst.FrequencyPenalty == nil {
dst.FrequencyPenalty = defaults.FrequencyPenalty
}
MergeMissingProviderOptions(&dst.ProviderOptions, defaults.ProviderOptions)
}
// MergeMissingProviderOptions fills unset provider option fields from defaults.
func MergeMissingProviderOptions(
dst **codersdk.ChatModelProviderOptions,
defaults *codersdk.ChatModelProviderOptions,
) {
if defaults == nil {
return
}
if *dst == nil {
copied := *defaults
*dst = &copied
return
}
current := *dst
for _, provider := range []string{
fantasyopenai.Name,
fantasyanthropic.Name,
fantasygoogle.Name,
fantasyopenaicompat.Name,
fantasyopenrouter.Name,
fantasyvercel.Name,
} {
switch provider {
case fantasyopenai.Name:
if defaults.OpenAI == nil {
continue
}
if current.OpenAI == nil {
copied := *defaults.OpenAI
current.OpenAI = &copied
continue
}
dstOpenAI := current.OpenAI
defaultOpenAI := defaults.OpenAI
if dstOpenAI.Include == nil {
dstOpenAI.Include = defaultOpenAI.Include
}
if dstOpenAI.Instructions == nil {
dstOpenAI.Instructions = defaultOpenAI.Instructions
}
if dstOpenAI.LogitBias == nil {
dstOpenAI.LogitBias = defaultOpenAI.LogitBias
}
if dstOpenAI.LogProbs == nil {
dstOpenAI.LogProbs = defaultOpenAI.LogProbs
}
if dstOpenAI.TopLogProbs == nil {
dstOpenAI.TopLogProbs = defaultOpenAI.TopLogProbs
}
if dstOpenAI.MaxToolCalls == nil {
dstOpenAI.MaxToolCalls = defaultOpenAI.MaxToolCalls
}
if dstOpenAI.ParallelToolCalls == nil {
dstOpenAI.ParallelToolCalls = defaultOpenAI.ParallelToolCalls
}
if dstOpenAI.User == nil {
dstOpenAI.User = defaultOpenAI.User
}
if dstOpenAI.ReasoningEffort == nil {
dstOpenAI.ReasoningEffort = defaultOpenAI.ReasoningEffort
}
if dstOpenAI.ReasoningSummary == nil {
dstOpenAI.ReasoningSummary = defaultOpenAI.ReasoningSummary
}
if dstOpenAI.MaxCompletionTokens == nil {
dstOpenAI.MaxCompletionTokens = defaultOpenAI.MaxCompletionTokens
}
if dstOpenAI.TextVerbosity == nil {
dstOpenAI.TextVerbosity = defaultOpenAI.TextVerbosity
}
if dstOpenAI.Prediction == nil {
dstOpenAI.Prediction = defaultOpenAI.Prediction
}
if dstOpenAI.Store == nil {
dstOpenAI.Store = defaultOpenAI.Store
}
if dstOpenAI.Metadata == nil {
dstOpenAI.Metadata = defaultOpenAI.Metadata
}
if dstOpenAI.PromptCacheKey == nil {
dstOpenAI.PromptCacheKey = defaultOpenAI.PromptCacheKey
}
if dstOpenAI.SafetyIdentifier == nil {
dstOpenAI.SafetyIdentifier = defaultOpenAI.SafetyIdentifier
}
if dstOpenAI.ServiceTier == nil {
dstOpenAI.ServiceTier = defaultOpenAI.ServiceTier
}
if dstOpenAI.StructuredOutputs == nil {
dstOpenAI.StructuredOutputs = defaultOpenAI.StructuredOutputs
}
if dstOpenAI.StrictJSONSchema == nil {
dstOpenAI.StrictJSONSchema = defaultOpenAI.StrictJSONSchema
}
case fantasyanthropic.Name:
if defaults.Anthropic == nil {
continue
}
if current.Anthropic == nil {
copied := *defaults.Anthropic
current.Anthropic = &copied
continue
}
dstAnthropic := current.Anthropic
defaultAnthropic := defaults.Anthropic
if dstAnthropic.SendReasoning == nil {
dstAnthropic.SendReasoning = defaultAnthropic.SendReasoning
}
if dstAnthropic.Thinking == nil {
dstAnthropic.Thinking = defaultAnthropic.Thinking
} else if defaultAnthropic.Thinking != nil &&
dstAnthropic.Thinking.BudgetTokens == nil {
dstAnthropic.Thinking.BudgetTokens = defaultAnthropic.Thinking.BudgetTokens
}
if dstAnthropic.Effort == nil {
dstAnthropic.Effort = defaultAnthropic.Effort
}
if dstAnthropic.DisableParallelToolUse == nil {
dstAnthropic.DisableParallelToolUse = defaultAnthropic.DisableParallelToolUse
}
case fantasygoogle.Name:
if defaults.Google == nil {
continue
}
if current.Google == nil {
copied := *defaults.Google
current.Google = &copied
continue
}
dstGoogle := current.Google
defaultGoogle := defaults.Google
if dstGoogle.ThinkingConfig == nil {
dstGoogle.ThinkingConfig = defaultGoogle.ThinkingConfig
} else if defaultGoogle.ThinkingConfig != nil {
if dstGoogle.ThinkingConfig.ThinkingBudget == nil {
dstGoogle.ThinkingConfig.ThinkingBudget = defaultGoogle.ThinkingConfig.ThinkingBudget
}
if dstGoogle.ThinkingConfig.IncludeThoughts == nil {
dstGoogle.ThinkingConfig.IncludeThoughts = defaultGoogle.ThinkingConfig.IncludeThoughts
}
}
if strings.TrimSpace(dstGoogle.CachedContent) == "" {
dstGoogle.CachedContent = defaultGoogle.CachedContent
}
if dstGoogle.SafetySettings == nil {
dstGoogle.SafetySettings = defaultGoogle.SafetySettings
}
if strings.TrimSpace(dstGoogle.Threshold) == "" {
dstGoogle.Threshold = defaultGoogle.Threshold
}
case fantasyopenaicompat.Name:
if defaults.OpenAICompat == nil {
continue
}
if current.OpenAICompat == nil {
copied := *defaults.OpenAICompat
current.OpenAICompat = &copied
continue
}
dstCompat := current.OpenAICompat
defaultCompat := defaults.OpenAICompat
if dstCompat.User == nil {
dstCompat.User = defaultCompat.User
}
if dstCompat.ReasoningEffort == nil {
dstCompat.ReasoningEffort = defaultCompat.ReasoningEffort
}
case fantasyopenrouter.Name:
if defaults.OpenRouter == nil {
continue
}
if current.OpenRouter == nil {
copied := *defaults.OpenRouter
current.OpenRouter = &copied
continue
}
dstRouter := current.OpenRouter
defaultRouter := defaults.OpenRouter
if dstRouter.Reasoning == nil {
dstRouter.Reasoning = defaultRouter.Reasoning
} else if defaultRouter.Reasoning != nil {
if dstRouter.Reasoning.Enabled == nil {
dstRouter.Reasoning.Enabled = defaultRouter.Reasoning.Enabled
}
if dstRouter.Reasoning.Exclude == nil {
dstRouter.Reasoning.Exclude = defaultRouter.Reasoning.Exclude
}
if dstRouter.Reasoning.MaxTokens == nil {
dstRouter.Reasoning.MaxTokens = defaultRouter.Reasoning.MaxTokens
}
if dstRouter.Reasoning.Effort == nil {
dstRouter.Reasoning.Effort = defaultRouter.Reasoning.Effort
}
}
if dstRouter.ExtraBody == nil {
dstRouter.ExtraBody = defaultRouter.ExtraBody
}
if dstRouter.IncludeUsage == nil {
dstRouter.IncludeUsage = defaultRouter.IncludeUsage
}
if dstRouter.LogitBias == nil {
dstRouter.LogitBias = defaultRouter.LogitBias
}
if dstRouter.LogProbs == nil {
dstRouter.LogProbs = defaultRouter.LogProbs
}
if dstRouter.ParallelToolCalls == nil {
dstRouter.ParallelToolCalls = defaultRouter.ParallelToolCalls
}
if dstRouter.User == nil {
dstRouter.User = defaultRouter.User
}
if dstRouter.Provider == nil {
dstRouter.Provider = defaultRouter.Provider
} else if defaultRouter.Provider != nil {
if dstRouter.Provider.Order == nil {
dstRouter.Provider.Order = defaultRouter.Provider.Order
}
if dstRouter.Provider.AllowFallbacks == nil {
dstRouter.Provider.AllowFallbacks = defaultRouter.Provider.AllowFallbacks
}
if dstRouter.Provider.RequireParameters == nil {
dstRouter.Provider.RequireParameters = defaultRouter.Provider.RequireParameters
}
if dstRouter.Provider.DataCollection == nil {
dstRouter.Provider.DataCollection = defaultRouter.Provider.DataCollection
}
if dstRouter.Provider.Only == nil {
dstRouter.Provider.Only = defaultRouter.Provider.Only
}
if dstRouter.Provider.Ignore == nil {
dstRouter.Provider.Ignore = defaultRouter.Provider.Ignore
}
if dstRouter.Provider.Quantizations == nil {
dstRouter.Provider.Quantizations = defaultRouter.Provider.Quantizations
}
if dstRouter.Provider.Sort == nil {
dstRouter.Provider.Sort = defaultRouter.Provider.Sort
}
}
case fantasyvercel.Name:
if defaults.Vercel == nil {
continue
}
if current.Vercel == nil {
copied := *defaults.Vercel
current.Vercel = &copied
continue
}
dstVercel := current.Vercel
defaultVercel := defaults.Vercel
if dstVercel.Reasoning == nil {
dstVercel.Reasoning = defaultVercel.Reasoning
} else if defaultVercel.Reasoning != nil {
if dstVercel.Reasoning.Enabled == nil {
dstVercel.Reasoning.Enabled = defaultVercel.Reasoning.Enabled
}
if dstVercel.Reasoning.MaxTokens == nil {
dstVercel.Reasoning.MaxTokens = defaultVercel.Reasoning.MaxTokens
}
if dstVercel.Reasoning.Effort == nil {
dstVercel.Reasoning.Effort = defaultVercel.Reasoning.Effort
}
if dstVercel.Reasoning.Exclude == nil {
dstVercel.Reasoning.Exclude = defaultVercel.Reasoning.Exclude
}
}
if dstVercel.ProviderOptions == nil {
dstVercel.ProviderOptions = defaultVercel.ProviderOptions
} else if defaultVercel.ProviderOptions != nil {
if dstVercel.ProviderOptions.Order == nil {
dstVercel.ProviderOptions.Order = defaultVercel.ProviderOptions.Order
}
if dstVercel.ProviderOptions.Models == nil {
dstVercel.ProviderOptions.Models = defaultVercel.ProviderOptions.Models
}
}
if dstVercel.User == nil {
dstVercel.User = defaultVercel.User
}
if dstVercel.LogitBias == nil {
dstVercel.LogitBias = defaultVercel.LogitBias
}
if dstVercel.LogProbs == nil {
dstVercel.LogProbs = defaultVercel.LogProbs
}
if dstVercel.TopLogProbs == nil {
dstVercel.TopLogProbs = defaultVercel.TopLogProbs
}
if dstVercel.ParallelToolCalls == nil {
dstVercel.ParallelToolCalls = defaultVercel.ParallelToolCalls
}
if dstVercel.ExtraBody == nil {
dstVercel.ExtraBody = defaultVercel.ExtraBody
}
}
}
}
// ModelFromConfig resolves a provider/model pair and constructs a fantasy
// language model client using the provided provider credentials.
func ModelFromConfig(
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/chatd/chatprovider"
"github.com/coder/coder/v2/codersdk"
)
func TestReasoningEffortFromChat(t *testing.T) {
@@ -76,6 +77,115 @@ func TestReasoningEffortFromChat(t *testing.T) {
}
}
func TestMergeMissingProviderOptions_OpenRouterNested(t *testing.T) {
t.Parallel()
options := &codersdk.ChatModelProviderOptions{
OpenRouter: &codersdk.ChatModelOpenRouterProviderOptions{
Reasoning: &codersdk.ChatModelOpenRouterReasoningOptions{
Enabled: boolPtr(true),
},
Provider: &codersdk.ChatModelOpenRouterProvider{
Order: []string{"openai"},
},
},
}
defaults := &codersdk.ChatModelProviderOptions{
OpenRouter: &codersdk.ChatModelOpenRouterProviderOptions{
Reasoning: &codersdk.ChatModelOpenRouterReasoningOptions{
Enabled: boolPtr(false),
Exclude: boolPtr(true),
MaxTokens: int64Ptr(123),
Effort: stringPtr("high"),
},
IncludeUsage: boolPtr(true),
Provider: &codersdk.ChatModelOpenRouterProvider{
Order: []string{"anthropic"},
AllowFallbacks: boolPtr(true),
RequireParameters: boolPtr(false),
DataCollection: stringPtr("allow"),
Only: []string{"openai"},
Ignore: []string{"foo"},
Quantizations: []string{"int8"},
Sort: stringPtr("latency"),
},
},
}
chatprovider.MergeMissingProviderOptions(&options, defaults)
require.NotNil(t, options)
require.NotNil(t, options.OpenRouter)
require.NotNil(t, options.OpenRouter.Reasoning)
require.True(t, *options.OpenRouter.Reasoning.Enabled)
require.Equal(t, true, *options.OpenRouter.Reasoning.Exclude)
require.EqualValues(t, 123, *options.OpenRouter.Reasoning.MaxTokens)
require.Equal(t, "high", *options.OpenRouter.Reasoning.Effort)
require.NotNil(t, options.OpenRouter.IncludeUsage)
require.True(t, *options.OpenRouter.IncludeUsage)
require.NotNil(t, options.OpenRouter.Provider)
require.Equal(t, []string{"openai"}, options.OpenRouter.Provider.Order)
require.NotNil(t, options.OpenRouter.Provider.AllowFallbacks)
require.True(t, *options.OpenRouter.Provider.AllowFallbacks)
require.NotNil(t, options.OpenRouter.Provider.RequireParameters)
require.False(t, *options.OpenRouter.Provider.RequireParameters)
require.Equal(t, "allow", *options.OpenRouter.Provider.DataCollection)
require.Equal(t, []string{"openai"}, options.OpenRouter.Provider.Only)
require.Equal(t, []string{"foo"}, options.OpenRouter.Provider.Ignore)
require.Equal(t, []string{"int8"}, options.OpenRouter.Provider.Quantizations)
require.Equal(t, "latency", *options.OpenRouter.Provider.Sort)
}
func TestMergeMissingCallConfig_FillsUnsetFields(t *testing.T) {
t.Parallel()
dst := codersdk.ChatModelCallConfig{
Temperature: float64Ptr(0.2),
ProviderOptions: &codersdk.ChatModelProviderOptions{
OpenAI: &codersdk.ChatModelOpenAIProviderOptions{
User: stringPtr("alice"),
},
},
}
defaults := codersdk.ChatModelCallConfig{
MaxOutputTokens: int64Ptr(512),
Temperature: float64Ptr(0.9),
TopP: float64Ptr(0.8),
ProviderOptions: &codersdk.ChatModelProviderOptions{
OpenAI: &codersdk.ChatModelOpenAIProviderOptions{
User: stringPtr("bob"),
ReasoningEffort: stringPtr("medium"),
},
},
}
chatprovider.MergeMissingCallConfig(&dst, defaults)
require.NotNil(t, dst.MaxOutputTokens)
require.EqualValues(t, 512, *dst.MaxOutputTokens)
require.NotNil(t, dst.Temperature)
require.Equal(t, 0.2, *dst.Temperature)
require.NotNil(t, dst.TopP)
require.Equal(t, 0.8, *dst.TopP)
require.NotNil(t, dst.ProviderOptions)
require.NotNil(t, dst.ProviderOptions.OpenAI)
require.Equal(t, "alice", *dst.ProviderOptions.OpenAI.User)
require.Equal(t, "medium", *dst.ProviderOptions.OpenAI.ReasoningEffort)
}
func stringPtr(value string) *string {
return &value
}
func boolPtr(value bool) *bool {
return &value
}
func int64Ptr(value int64) *int64 {
return &value
}
func float64Ptr(value float64) *float64 {
return &value
}