fix: gate reasoning_effort by LiteLLM model registry (closes #517) (#523)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ahmed Allam
2026-06-07 12:24:48 -07:00
committed by GitHub
parent 1aad460f6e
commit 13046cc74a
3 changed files with 26 additions and 3 deletions
+14
View File
@@ -99,3 +99,17 @@ def uses_chat_completions_tool_schema(model_name: str, settings: Settings) -> bo
if model.startswith(("litellm/", "any-llm/")): if model.startswith(("litellm/", "any-llm/")):
return True return True
return bool(settings.llm.api_base) return bool(settings.llm.api_base)
def model_supports_reasoning(model_name: str) -> bool:
import litellm
name = model_name.strip().lower()
for prefix in ("litellm/", "any-llm/"):
if name.startswith(prefix):
name = name[len(prefix) :]
break
entry = litellm.model_cost.get(name)
if entry is None and "/" in name:
entry = litellm.model_cost.get(name.rsplit("/", 1)[1])
return bool(entry and entry.get("supports_reasoning"))
+8 -2
View File
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any
from agents.model_settings import ModelSettings from agents.model_settings import ModelSettings
from openai.types.shared import Reasoning from openai.types.shared import Reasoning
from strix.config.models import DEFAULT_MODEL_RETRY from strix.config.models import DEFAULT_MODEL_RETRY, model_supports_reasoning
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -108,13 +108,19 @@ def build_scope_context(scan_config: dict[str, Any]) -> dict[str, Any]:
def make_model_settings( def make_model_settings(
reasoning_effort: ReasoningEffort | None, reasoning_effort: ReasoningEffort | None,
*,
model_name: str,
) -> ModelSettings: ) -> ModelSettings:
# Anthropic + DeepSeek thinking reject ``tool_choice="required"`` outright # Anthropic + DeepSeek thinking reject ``tool_choice="required"`` outright
# when reasoning is enabled; OpenAI o-series accepts both but doesn't need # when reasoning is enabled; OpenAI o-series accepts both but doesn't need
# the safety net. When reasoning is on we let the model self-select tools # the safety net. When reasoning is on we let the model self-select tools
# and rely on the system prompt + the ``_finish_tool_use_behavior`` callback # and rely on the system prompt + the ``_finish_tool_use_behavior`` callback
# to keep the loop converging on a lifecycle tool. # to keep the loop converging on a lifecycle tool.
use_reasoning = reasoning_effort is not None and reasoning_effort != "none" use_reasoning = (
reasoning_effort is not None
and reasoning_effort != "none"
and model_supports_reasoning(model_name)
)
model_settings = ModelSettings( model_settings = ModelSettings(
parallel_tool_calls=False, parallel_tool_calls=False,
tool_choice=None if use_reasoning else "required", tool_choice=None if use_reasoning else "required",
+4 -1
View File
@@ -153,7 +153,10 @@ async def run_strix_scan(
is_whitebox = any(t.get("type") == "local_code" for t in targets) is_whitebox = any(t.get("type") == "local_code" for t in targets)
skills = list(scan_config.get("skills") or []) skills = list(scan_config.get("skills") or [])
root_task = build_root_task(scan_config) root_task = build_root_task(scan_config)
model_settings = make_model_settings(settings.llm.reasoning_effort) model_settings = make_model_settings(
settings.llm.reasoning_effort,
model_name=resolved_model,
)
run_config = RunConfig( run_config = RunConfig(
model=resolved_model, model=resolved_model,
model_settings=model_settings, model_settings=model_settings,