From ccc65f484a26912346f6cd4ec8cd57b031d86281 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 23:10:01 +0900 Subject: [PATCH 1/4] fix(strix): disable effort for direct OpenAI tool fallback --- .../strix-openai-fallback-api-base-routing.md | 15 ++++++++++++++- scripts/ci/strix_quick_gate.sh | 8 ++++++++ scripts/ci/test_strix_quick_gate.sh | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/doctoring/strix-openai-fallback-api-base-routing.md b/docs/doctoring/strix-openai-fallback-api-base-routing.md index d38d222d4..4472eedeb 100644 --- a/docs/doctoring/strix-openai-fallback-api-base-routing.md +++ b/docs/doctoring/strix-openai-fallback-api-base-routing.md @@ -19,6 +19,17 @@ 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 the explicit direct-OpenAI child attempt. +NVIDIA and other provider attempts retain the workflow's configured high +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) @@ -70,7 +81,9 @@ Regression evidence proves that: 7. a non-https override fails configuration (exit 2) instead of scanning; 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 +10. explicit direct-OpenAI attempts receive reasoning effort `none`, while + the other provider attempts retain their configured effort; and +11. 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. ## Limitations diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index c9aa41545..c01e2bd4f 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -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 @@ -2574,6 +2575,12 @@ 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"; then + child_reasoning_effort="none" + fi if ! resolved_target_path="$(resolve_current_target_path "$TARGET_PATH")"; then return 1 fi @@ -2604,6 +2611,7 @@ run_strix_once() { STRIX_CHILD_EXECUTABLE_ROOT="$STRIX_EXECUTABLE_ROOT" \ STRIX_CHILD_EXECUTABLE_SHA256="$STRIX_EXECUTABLE_SHA256" \ STRIX_CHILD_REQUIRE_EXECUTABLE_INTEGRITY="${IS_PR_EVIDENCE_RUN:-false}" \ + STRIX_REASONING_EFFORT="$child_reasoning_effort" \ python3 - "$timeout_seconds" "$resolved_target_path" "$SCAN_MODE" "$STRIX_LOG" "$STRIX_SCAN_WORKING_DIR" <<'PY' import hashlib import hmac diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 3d3449dae..9c6ebf39d 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -3415,6 +3415,10 @@ REPORT exit 1 ;; openai/gpt-5.4) + if [ "${STRIX_REASONING_EFFORT:-}" != "none" ]; then + echo "direct OpenAI function-tools fallback requires reasoning effort none" >&2 + exit 29 + fi if [ "${LLM_API_KEY:-}" != "openai-fallback-token" ]; then echo "unexpected direct-OpenAI fallback key (${LLM_API_KEY:-})" >&2 exit 26 From 6c26f754bca07b1fab1b7bf8b3343f5a70647493 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 23:17:04 +0900 Subject: [PATCH 2/4] fix(strix): preserve custom endpoint effort --- .../strix-openai-fallback-api-base-routing.md | 5 +-- scripts/ci/strix_quick_gate.sh | 3 +- scripts/ci/test_strix_quick_gate.sh | 33 +++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/doctoring/strix-openai-fallback-api-base-routing.md b/docs/doctoring/strix-openai-fallback-api-base-routing.md index 4472eedeb..c4c56464f 100644 --- a/docs/doctoring/strix-openai-fallback-api-base-routing.md +++ b/docs/doctoring/strix-openai-fallback-api-base-routing.md @@ -21,8 +21,9 @@ 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 the explicit direct-OpenAI child attempt. -NVIDIA and other provider attempts retain the workflow's configured high +`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: diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index c01e2bd4f..3f6d386dd 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -2578,7 +2578,8 @@ run_strix_once() { # 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"; then + 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 if ! resolved_target_path="$(resolve_current_target_path "$TARGET_PATH")"; then diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 9c6ebf39d..16e9631be 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -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 ;; @@ -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" @@ -5876,6 +5876,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=" \ "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 \ @@ -6159,6 +6165,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" + ;; 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" \ @@ -12525,6 +12543,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" \ "" \ From a5b95079225ad77634690c487872693d5ecbba2b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 01:22:03 +0900 Subject: [PATCH 3/4] test(strix): isolate fallback API base --- scripts/ci/test_strix_quick_gate.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 947c1b668..f64af2334 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -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 ) From 4be977702da6c24dc6f6290463ab9bc49c8a4c4b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 01:26:53 +0900 Subject: [PATCH 4/4] fix(strix): remove redundant effort export --- scripts/ci/strix_quick_gate.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 8efb8e557..c97a66ef9 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -2613,7 +2613,6 @@ run_strix_once() { STRIX_CHILD_EXECUTABLE_ROOT="$STRIX_EXECUTABLE_ROOT" \ STRIX_CHILD_EXECUTABLE_SHA256="$STRIX_EXECUTABLE_SHA256" \ STRIX_CHILD_REQUIRE_EXECUTABLE_INTEGRITY="${IS_PR_EVIDENCE_RUN:-false}" \ - STRIX_REASONING_EFFORT="$child_reasoning_effort" \ python3 - "$timeout_seconds" "$resolved_target_path" "$SCAN_MODE" "$STRIX_LOG" "$STRIX_SCAN_WORKING_DIR" <<'PY' import hashlib import hmac