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
21 changes: 16 additions & 5 deletions docs/doctoring/strix-openai-fallback-api-base-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ fallback key file, so every provider chain that ends in
`openai-direct/gpt-5.4` (NVIDIA NIM primary, OpenRouter primary,
GitHub Models primary) inherits correct routing automatically.

Strix invokes function tools through chat completions. Direct OpenAI rejects
those tools when `reasoning_effort` is non-none, so the gate scopes
`STRIX_REASONING_EFFORT=none` to an explicit direct-OpenAI child using the
native OpenAI endpoint. NVIDIA, other providers, and standalone explicit-model
runs targeting a custom OpenAI-compatible endpoint retain their configured
effort.

Contextual-orchestrator PR #881 run `32967361853` demonstrated this boundary:
the NVIDIA attempts exhausted with 429/410 provider responses, then the direct
OpenAI fallback reached the correct endpoint but returned HTTP 400 because
function tools were combined with `reasoning_effort=high`.

## Failure this fixes

Required-CI evidence (BandScope PR #1021 strix run 32800796577, 2026-08-25)
Expand Down Expand Up @@ -71,11 +83,10 @@ Regression evidence proves that:
8. the workflow provisions the override file and passes it into the gate env;
9. the required-workflow smoke contract pins both sides of the wiring; and
10. the stale `gpt-5.6-luna` expectations left behind by the model rename are
aligned with the valid `gpt-5.4` contract in queue-contract tests; and
11. the direct GPT-5.4 fallback clears reasoning effort to `none` because the
pinned Strix scanner uses Chat Completions function tools, a combination
the provider rejects when `reasoning_effort` is present. Other models keep
the workflow's configured `high` effort.
aligned with the valid `gpt-5.4` contract in queue-contract tests; and
11. explicit direct-OpenAI attempts on the native endpoint receive reasoning
effort `none`, while custom compatible endpoints and other providers retain
their configured effort.

## Limitations

Expand Down
16 changes: 8 additions & 8 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2551,6 +2551,7 @@ run_strix_once() {
local rc
local llm_api_base_value
local child_model
local child_reasoning_effort="${STRIX_REASONING_EFFORT:-}"
local resolved_target_path
local timeout_seconds="$STRIX_PROCESS_TIMEOUT_SECONDS"
local total_budget_limited_timeout=0
Expand All @@ -2574,20 +2575,19 @@ run_strix_once() {
return 2
fi
child_model="$(child_model_for_api_base "$model" "$llm_api_base_value")"
# Strix uses function tools. Direct OpenAI chat-completions rejects those
# tools when reasoning_effort is non-none, so scope the supported value to
# this provider attempt without weakening reasoning on other providers.
if is_explicit_openai_model "$model" &&
{ [ -z "$llm_api_base_value" ] || [ "${llm_api_base_value%/}" = "https://api.openai.com/v1" ]; }; then
child_reasoning_effort="none"
fi
Comment thread
seonghobae marked this conversation as resolved.
if ! resolved_target_path="$(resolve_current_target_path "$TARGET_PATH")"; then
return 1
fi
local start_epoch
start_epoch="$(date +%s)"
local child_llm_api_key=""
local child_reasoning_effort="${STRIX_REASONING_EFFORT:-}"
case "$(normalize_model "$model")" in
openai-direct/gpt-5.4 | openai_direct/gpt-5.4)
# OpenAI rejects function tools plus reasoning_effort for GPT-5.4 on
# Chat Completions, the transport used by the pinned Strix scanner.
child_reasoning_effort="none"
;;
esac
if ! is_vertex_model "$(normalize_model "$model")"; then
child_llm_api_key="$LLM_API_KEY"
if is_github_models_model "$(normalize_model "$model")" && [ -n "$STRIX_GITHUB_MODELS_KEY" ]; then
Expand Down
36 changes: 33 additions & 3 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3332,7 +3332,7 @@ printf '%s\n' "$target_path" >> "${FAKE_STRIX_TARGET_LOG:?}"
STRIX_REPORTS_DIR="${STRIX_REPORTS_DIR:-strix_runs}"

case "${FAKE_STRIX_SCENARIO:?}" in
success|runtime-env-forwarding|vertex-primary-success-timing-message|direct-openai-gpt-does-not-require-github-models-api-base|pr-executable-integrity-mismatch|pr-executable-group-writable)
success|runtime-env-forwarding|custom-openai-compatible-preserves-effort|vertex-primary-success-timing-message|direct-openai-gpt-does-not-require-github-models-api-base|pr-executable-integrity-mismatch|pr-executable-group-writable)
echo "scan ok"
exit 0
;;
Expand Down Expand Up @@ -3416,7 +3416,7 @@ REPORT
;;
openai/gpt-5.4)
if [ "${STRIX_REASONING_EFFORT:-}" != "none" ]; then
echo "direct OpenAI GPT-5.4 fallback retained unsupported reasoning effort (${STRIX_REASONING_EFFORT:-<unset>})" >&2
echo "direct OpenAI function-tools fallback requires reasoning effort none" >&2
exit 29
fi
if [ "${LLM_API_KEY:-}" != "openai-fallback-token" ]; then
Expand Down Expand Up @@ -5660,7 +5660,7 @@ PY
STRIX_REPORTS_DIR="$repo_root_dir/strix_runs"
STRIX_TARGET_PATH="$effective_target_path"
)
if [ "$scenario" = "runtime-env-forwarding" ]; then
if [ "$scenario" = "runtime-env-forwarding" ] || [ "$scenario" = "custom-openai-compatible-preserves-effort" ]; then
env_cmd+=(
LLM_TIMEOUT="90"
STRIX_MEMORY_COMPRESSOR_TIMEOUT="10"
Expand Down Expand Up @@ -5808,6 +5808,7 @@ PY
-u STRIX_GEMINI_FALLBACK_MODELS \
-u STRIX_FALLBACK_MODELS \
-u STRIX_OPENAI_FALLBACK_KEY_FILE \
-u STRIX_OPENAI_FALLBACK_API_BASE_FILE \
"${env_cmd[@]}" \
bash "./scripts/ci/strix_quick_gate.sh" >"$output_log" 2>&1
)
Expand Down Expand Up @@ -5877,6 +5878,12 @@ PY
"LLM_TIMEOUT=90;STRIX_MEMORY_COMPRESSOR_TIMEOUT=10;STRIX_REASONING_EFFORT=minimal;STRIX_LLM_MAX_RETRIES=1;GEMINI_LOCATION=GLOBAL;PYTHONWARNINGS=ignore:Pydantic serializer warnings:UserWarning:pydantic.main;NPM_CONFIG_IGNORE_SCRIPTS=true;PNPM_CONFIG_IGNORE_SCRIPTS=true;YARN_ENABLE_SCRIPTS=false;UNRELATED_SECRET=<unset>" \
"scenario=$scenario runtime env forwarding"
fi
if [ "$scenario" = "custom-openai-compatible-preserves-effort" ]; then
assert_file_contains \
"$runtime_env_log" \
"STRIX_REASONING_EFFORT=minimal" \
"scenario=$scenario custom compatible endpoint effort"
fi

if [ "$scenario" = "report-known-internal-warning-sanitized" ]; then
assert_file_not_contains \
Expand Down Expand Up @@ -6160,6 +6167,18 @@ run_filtered_gate_case_if_requested() {
"" \
"github_models/deepseek/deepseek-v3-0324 github_models/deepseek/deepseek-r1-0528"
;;
custom-openai-compatible-preserves-effort)
run_gate_case "custom-openai-compatible-preserves-effort" \
"openai-direct/gpt-5.4" \
"" \
"0" \
"scan ok" \
"1" \
"openai/gpt-5.4" \
"https://compatible.example/v1" \
"openai" \
"https://compatible.example/v1"
;;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
nvidia-rate-limit-openai-direct-fallback-clears-api-base)
run_gate_case_allow_provider_signal "nvidia-rate-limit-openai-direct-fallback-clears-api-base" \
"nvidia_nim/nvidia/rate-limited-primary" \
Expand Down Expand Up @@ -12526,6 +12545,17 @@ run_gate_case "github-models-model-prefix-requires-api-base" \
"openai" \
""

run_gate_case "custom-openai-compatible-preserves-effort" \
"openai-direct/gpt-5.4" \
"" \
"0" \
"scan ok" \
"1" \
"openai/gpt-5.4" \
"https://compatible.example/v1" \
"openai" \
"https://compatible.example/v1"

run_gate_case "github-models-api-base-rejected-for-direct-openai" \
"openai/o4-mini" \
"" \
Expand Down
Loading