Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/schemas/modelcapabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type ModelCapabilities struct {
SupportsReasoning *bool `json:"supports_reasoning,omitempty"`
SupportsResponseSchema *bool `json:"supports_response_schema,omitempty"`
SupportsReasoningWithToolCalls *bool `json:"supports_reasoning_with_tool_calls,omitempty"`
SupportsNoneReasoningEffort *bool `json:"supports_none_reasoning_effort,omitempty"`
SupportsServiceTier *bool `json:"supports_service_tier,omitempty"`
SupportsPromptCaching *bool `json:"supports_prompt_caching,omitempty"`
SupportsWebSearch *bool `json:"supports_web_search,omitempty"`
Expand Down
17 changes: 17 additions & 0 deletions framework/modelcatalog/datasheet/capabilities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,20 @@ func TestExtractSupportedParams_WebSearchAbsent(t *testing.T) {
}
}
}

// TestExtractSupportedParams_NoneReasoningEffort guards the
// supports_none_reasoning_effort flag: compat's dropUnsupportedParams uses it
// to decide whether to force reasoning.effort to "none" (vs. dropping
// reasoning entirely) for models that reason by default even when
// reasoning_effort is omitted.
func TestExtractSupportedParams_NoneReasoningEffort(t *testing.T) {
if got := extractSupportedParams(&schemas.ModelCapabilities{SupportsNoneReasoningEffort: capabilityBoolPtr(true)}); !slices.Contains(got, "supports_none_reasoning_effort") {
t.Errorf("expected supported params to contain \"supports_none_reasoning_effort\", got %v", got)
}
if got := extractSupportedParams(&schemas.ModelCapabilities{SupportsNoneReasoningEffort: capabilityBoolPtr(false)}); slices.Contains(got, "supports_none_reasoning_effort") {
t.Errorf("expected supported params to omit \"supports_none_reasoning_effort\", got %v", got)
}
if got := extractSupportedParams(&schemas.ModelCapabilities{}); slices.Contains(got, "supports_none_reasoning_effort") {
t.Errorf("expected supported params to omit \"supports_none_reasoning_effort\" when unset, got %v", got)
}
}
3 changes: 3 additions & 0 deletions framework/modelcatalog/datasheet/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@ func extractSupportedParams(parsed *schemas.ModelCapabilities) []string {
if parsed.SupportsReasoningWithToolCalls == nil || *parsed.SupportsReasoningWithToolCalls {
addParam("reasoning_with_tool_calls")
}
if parsed.SupportsNoneReasoningEffort != nil && *parsed.SupportsNoneReasoningEffort {
addParam("supports_none_reasoning_effort")
}
if parsed.SupportsResponseSchema != nil && *parsed.SupportsResponseSchema {
addParam("response_format")
addParam("text")
Expand Down
17 changes: 15 additions & 2 deletions plugins/compat/dropparams.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,23 @@ func dropUnsupportedParams(ctx *schemas.BifrostContext, req *schemas.BifrostRequ
if params.Reasoning != nil {
// for chat completions, some models do not support reasoning_effort
// with tools
if !isSupported["reasoning"] || (hasSupportedTools && !isSupported["reasoning_with_tool_calls"]) {
if !isSupported["reasoning"] {
params.Reasoning = nil
dropped = append(dropped, "reasoning")
} else if hasSupportedTools && !isSupported["reasoning_with_tool_calls"] {
// models like gpt-5.6 series models defaults to reasoning, even when
// reasoning_effort is not set.
if isSupported["supports_none_reasoning_effort"] {
params.Reasoning = &schemas.ChatReasoning{Effort: new("none")}
dropped = append(dropped, "reasoning")
} else {
params.Reasoning = nil
dropped = append(dropped, "reasoning")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
} else if isSupported["reasoning"] && isSupported["supports_none_reasoning_effort"] && hasSupportedTools && !isSupported["reasoning_with_tool_calls"] {
params.Reasoning = &schemas.ChatReasoning{Effort: new("none")}
dropped = append(dropped, "reasoning")
}
if params.ResponseFormat != nil && !isSupported["response_format"] {
params.ResponseFormat = nil
Expand Down Expand Up @@ -364,4 +377,4 @@ func applyBedrockResponsesCompatibility(req *schemas.BifrostResponsesRequest) []
msg.Content.ContentBlocks = kept
}
return dropped
}
}
72 changes: 69 additions & 3 deletions plugins/compat/dropparams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,14 +289,80 @@ func TestDropUnsupportedParams_ChatReasoningWithUnsupportedTools(t *testing.T) {
}

dropReasoning := newChat()
dropped = dropUnsupportedParams(newTestContext(), dropReasoning, []string{"reasoning", "tools"})
if dropReasoning.ChatRequest.Params.Reasoning != nil {
t.Fatalf("reasoning = preserved, want dropped when tools survive without reasoning_with_tool_calls")
dropped = dropUnsupportedParams(newTestContext(), dropReasoning, []string{"reasoning", "tools", "supports_none_reasoning_effort"})
if dropReasoning.ChatRequest.Params.Reasoning == nil {
t.Fatalf("reasoning = nil, want forced to effort=none when tools survive without reasoning_with_tool_calls")
}
if got := dropReasoning.ChatRequest.Params.Reasoning.Effort; got == nil || *got != "none" {
t.Fatalf("reasoning.effort = %v, want \"none\"", got)
}
if dropReasoning.ChatRequest.Params.Tools == nil {
t.Fatalf("tools = dropped, want preserved")
}
if !slices.Contains(dropped, "reasoning") {
t.Errorf("reasoning not reported in dropped=%v, want present", dropped)
}

dropReasoningNoNoneSupport := newChat()
dropped = dropUnsupportedParams(newTestContext(), dropReasoningNoNoneSupport, []string{"reasoning", "tools"})
if dropReasoningNoNoneSupport.ChatRequest.Params.Reasoning != nil {
t.Fatalf("reasoning = %v, want dropped to nil when model doesn't support effort=none", dropReasoningNoNoneSupport.ChatRequest.Params.Reasoning)
}
if !slices.Contains(dropped, "reasoning") {
t.Errorf("reasoning not reported in dropped=%v, want present", dropped)
}
}

func TestDropUnsupportedParams_ChatReasoningNilForcedToNoneWithUnsupportedTools(t *testing.T) {
newChatNoReasoning := func() *schemas.BifrostRequest {
return &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "reasoning-no-tools-model",
Params: &schemas.ChatParameters{
Tools: []schemas.ChatTool{{
Type: schemas.ChatToolTypeFunction,
Function: &schemas.ChatToolFunction{
Name: "get_weather",
Description: schemas.Ptr("Returns weather"),
},
}},
},
},
}
}

forceNone := newChatNoReasoning()
dropped := dropUnsupportedParams(newTestContext(), forceNone, []string{"reasoning", "tools", "supports_none_reasoning_effort"})
if forceNone.ChatRequest.Params.Reasoning == nil {
t.Fatalf("reasoning = nil, want forced to effort=none when model reasons by default and doesn't support reasoning with tools")
}
if got := forceNone.ChatRequest.Params.Reasoning.Effort; got == nil || *got != "none" {
t.Fatalf("reasoning.effort = %v, want \"none\"", got)
}
if forceNone.ChatRequest.Params.Tools == nil {
t.Fatalf("tools = dropped, want preserved")
}
if !slices.Contains(dropped, "reasoning") {
t.Errorf("reasoning not reported in dropped=%v, want present", dropped)
}

noNoneSupport := newChatNoReasoning()
dropped = dropUnsupportedParams(newTestContext(), noNoneSupport, []string{"reasoning", "tools"})
if noNoneSupport.ChatRequest.Params.Reasoning != nil {
t.Fatalf("reasoning = %v, want left nil when model doesn't support effort=none", noNoneSupport.ChatRequest.Params.Reasoning)
}
if slices.Contains(dropped, "reasoning") {
t.Errorf("reasoning reported in dropped=%v, want absent since it was never set", dropped)
}

reasoningWithToolsSupported := newChatNoReasoning()
dropped = dropUnsupportedParams(newTestContext(), reasoningWithToolsSupported, []string{"reasoning", "tools", "reasoning_with_tool_calls", "supports_none_reasoning_effort"})
if reasoningWithToolsSupported.ChatRequest.Params.Reasoning != nil {
t.Fatalf("reasoning = %v, want left nil when reasoning_with_tool_calls is supported", reasoningWithToolsSupported.ChatRequest.Params.Reasoning)
}
if slices.Contains(dropped, "reasoning") {
t.Errorf("reasoning reported in dropped=%v, want absent since it was never set", dropped)
}
}
Loading