From 5ecbebb0d4bee769826302c24ac00a00c799521b Mon Sep 17 00:00:00 2001 From: seonghobae Date: Mon, 24 Aug 2026 18:56:58 +0900 Subject: [PATCH 1/4] fix(strix): route openai-direct fallback to the OpenAI API base 0c6b9a64 mapped the openai-direct prefix and routed the OpenAI key, but the child scan still read LLM_API_BASE_FILE -- the primary provider's endpoint. Observed on LineageWeave#570 (run 32701426812): after both NVIDIA models 429'd, the openai-direct/gpt-5.6-luna fallback reached integrate.api.nvidia.com with an unknown model path and died in 4s with '404 page not found'. When the candidate is explicit-openai and STRIX_OPENAI_FALLBACK_API_BASE_FILE is configured, select that file as the api-base source, exactly mirroring the existing GitHub Models cross-provider routing. Key routing was already correct. --- scripts/ci/strix_quick_gate.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 36ec3e5f8e..fa676a0d5a 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -2417,7 +2417,14 @@ resolved_llm_api_base_for_model() { local api_base_file="$LLM_API_BASE_FILE" local api_base_file_name="LLM_API_BASE_FILE" - if is_github_models_model "$model" && [ -n "${STRIX_GITHUB_MODELS_API_BASE_FILE:-}" ]; then + if is_explicit_openai_model "$model" && [ -n "${STRIX_OPENAI_FALLBACK_API_BASE_FILE:-}" ]; then + # Cross-provider fallback: openai-direct/* candidates must reach the + # direct OpenAI API even when the primary provider selected a + # different LLM_API_BASE_FILE endpoint (e.g. NVIDIA NIM). Without + # this the fallback hits the primary gateway and 404s. + api_base_file="$STRIX_OPENAI_FALLBACK_API_BASE_FILE" + api_base_file_name="STRIX_OPENAI_FALLBACK_API_BASE_FILE" + elif is_github_models_model "$model" && [ -n "${STRIX_GITHUB_MODELS_API_BASE_FILE:-}" ]; then # Cross-provider fallback: when the active primary provider uses a # different API base (for example OpenRouter), github_models/* fallback # attempts must still route through the GitHub Models inference endpoint. From 84eb37943d2b8ad57fd405c858721f71e3144ca6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 19:15:47 +0900 Subject: [PATCH 2/4] fix(strix): wire direct OpenAI fallback endpoint --- .github/workflows/strix.yml | 4 ++++ scripts/ci/test_strix_quick_gate.sh | 2 ++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index c8c054e422..8eb5be301f 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -706,6 +706,9 @@ jobs: openai_fallback_key_file="$RUNNER_TEMP/openai_fallback_key.txt" printf '%s' "$openai_trimmed" > "$openai_fallback_key_file" echo "STRIX_OPENAI_FALLBACK_KEY_FILE=$openai_fallback_key_file" >> "$GITHUB_ENV" + openai_fallback_api_base_file="$RUNNER_TEMP/openai_fallback_api_base.txt" + printf '%s' 'https://api.openai.com/v1' > "$openai_fallback_api_base_file" + echo "STRIX_OPENAI_FALLBACK_API_BASE_FILE=$openai_fallback_api_base_file" >> "$GITHUB_ENV" fi - name: Prepare Vertex AI credentials @@ -838,6 +841,7 @@ jobs: STRIX_GITHUB_MODELS_API_BASE_FILE: ${{ env.STRIX_GITHUB_MODELS_API_BASE_FILE }} STRIX_GITHUB_MODELS_KEY_FILE: ${{ env.STRIX_GITHUB_MODELS_KEY_FILE }} STRIX_OPENAI_FALLBACK_KEY_FILE: ${{ env.STRIX_OPENAI_FALLBACK_KEY_FILE }} + STRIX_OPENAI_FALLBACK_API_BASE_FILE: ${{ env.STRIX_OPENAI_FALLBACK_API_BASE_FILE }} STRIX_FAIL_ON_PROVIDER_SIGNAL: "1" STRIX_VERTEX_FALLBACK_MODELS: "" NPM_CONFIG_IGNORE_SCRIPTS: "true" diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 945eb3fb30..cc601e5dc1 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -365,6 +365,8 @@ assert_strix_workflow_pr_trigger_hardened() { assert_file_contains "$workflow_file" "steps.gate.outputs.provider_mode == 'nvidia_nim' && 'nvidia_nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 openai-direct/gpt-5.6-luna'" "strix workflow gives NVIDIA NIM scans contracted fallbacks" assert_file_not_contains "$workflow_file" "STRIX_FALLBACK_MODELS: \${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/openai/o3" "strix workflow fallback list must not depend on GitHub Models, which is in platform-wide retirement" assert_file_contains "$workflow_file" "Prepare GitHub Models fallback credentials" "strix workflow provisions GitHub Models fallback credentials for direct-OpenAI scans" + assert_file_contains "$workflow_file" "STRIX_OPENAI_FALLBACK_API_BASE_FILE" "strix workflow routes direct-OpenAI fallbacks through a trusted API base file" + assert_file_contains "$workflow_file" "https://api.openai.com/v1" "strix workflow uses the OpenAI platform endpoint for direct fallbacks" assert_file_contains "$GATE_SCRIPT" "STRIX_GITHUB_MODELS_KEY_FILE" "strix gate reads the optional GitHub Models fallback key file" assert_file_contains "$GATE_SCRIPT" "STRIX_GITHUB_MODELS_API_BASE_FILE" "strix gate routes github_models fallback models through the GitHub Models endpoint" assert_file_not_contains "$workflow_file" 'github_models/deepseek/deepseek-r1-0528 | github_models/deepseek/deepseek-v3-0324)' "strix workflow keeps DeepSeek GitHub Models restricted to fallback-only routing" From 0d353001a54caa9f26cac6a22111cabd3ba83e11 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 19:49:11 +0900 Subject: [PATCH 3/4] test(strix): cover direct OpenAI fallback endpoint --- scripts/ci/test_strix_quick_gate.sh | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index cc601e5dc1..173ec13df1 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -3423,6 +3423,30 @@ REPORT ;; esac ;; + nvidia-nim-openai-direct-fallback-success) + case "${STRIX_LLM:-}" in + nvidia_nim/primary-model) + if [ "${LLM_API_KEY:-}" != "dummy" ] || [ "${LLM_API_BASE:-}" != "https://integrate.api.nvidia.com/v1" ]; then + echo "unexpected NVIDIA NIM primary credentials or endpoint" >&2 + exit 17 + fi + echo "nvidia_nim.RateLimitError: Error code: 429 - provider quota exhausted" + exit 1 + ;; + openai/gpt-5.6-luna) + if [ "${LLM_API_KEY:-}" != "openai-fallback-key" ] || [ "${LLM_API_BASE:-}" != "https://api.openai.com/v1" ]; then + echo "unexpected direct-OpenAI fallback credentials or endpoint" >&2 + exit 18 + fi + echo "scan ok after direct-OpenAI fallback" + exit 0 + ;; + *) + echo "unexpected NVIDIA-to-OpenAI fallback model ${STRIX_LLM:-}" >&2 + exit 19 + ;; + esac + ;; vertex-all-notfound) echo "Error: litellm.NotFoundError: Vertex_aiException - x" echo '"status": "NOT_FOUND"' @@ -5650,6 +5674,12 @@ PY env_cmd+=(STRIX_GITHUB_MODELS_API_BASE_FILE="$tmp_dir/github_models_api_base.txt") env_cmd+=(STRIX_GITHUB_MODELS_KEY_FILE="$tmp_dir/github_models_key.txt") fi + if [ "$scenario" = "nvidia-nim-openai-direct-fallback-success" ]; then + printf '%s' 'openai-fallback-key' >"$tmp_dir/openai_fallback_key.txt" + printf '%s' 'https://api.openai.com/v1' >"$tmp_dir/openai_fallback_api_base.txt" + env_cmd+=(STRIX_OPENAI_FALLBACK_KEY_FILE="$tmp_dir/openai_fallback_key.txt") + env_cmd+=(STRIX_OPENAI_FALLBACK_API_BASE_FILE="$tmp_dir/openai_fallback_api_base.txt") + fi if [ "$min_fail_severity" = "__UNSET__" ]; then local next_env_cmd=() local env_pair @@ -5904,6 +5934,37 @@ run_gate_case_allow_provider_signal() { run_gate_case_with_provider_signal_mode "0" "$@" } +run_nvidia_nim_openai_direct_fallback_case() { + run_gate_case_allow_provider_signal "nvidia-nim-openai-direct-fallback-success" \ + "nvidia_nim/primary-model" \ + "" \ + "0" \ + "REGEX:Strix quick scan succeeded with fallback model 'openai-direct/gpt-5.6-luna' in [0-9]+s\\." \ + "2" \ + "nvidia_nim/primary-model|openai/gpt-5.6-luna" \ + "https://integrate.api.nvidia.com/v1|https://api.openai.com/v1" \ + "nvidia_nim" \ + "https://integrate.api.nvidia.com/v1" \ + "" \ + "0" \ + "CRITICAL" \ + "0" \ + "" \ + "" \ + "1200" \ + "0" \ + "" \ + "" \ + "" \ + "" \ + "0" \ + "" \ + "" \ + "" \ + "__SAME_AS_FALLBACK_MODELS__" \ + "openai-direct/gpt-5.6-luna" +} + run_github_models_http410_case() { local scenario="$1" local expected_exit="$2" @@ -6126,6 +6187,9 @@ run_filtered_gate_case_if_requested() { "" \ "github_models/openai/o3" ;; + nvidia-nim-openai-direct-fallback-success) + run_nvidia_nim_openai_direct_fallback_case + ;; gemini-timeout-fallback-success) run_gate_case_allow_provider_signal "gemini-timeout-fallback-success" \ "gemini/timeout-fallback-primary" \ @@ -12516,6 +12580,10 @@ run_gate_case "openai-direct-quota-github-models-fallback-success" \ "" \ "github_models/openai/o3" +# NVIDIA NIM primary scans must route an explicit direct-OpenAI fallback to +# the OpenAI endpoint and its own credential, not the primary provider input. +run_nvidia_nim_openai_direct_fallback_case + run_gate_case "github-models-fallback-success-deepseek-v3" \ "vertex_ai/missing-primary" \ "github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324" \ From b92648e1df52a9213f46dcbe88e9fc448f6d54a7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 19:56:40 +0900 Subject: [PATCH 4/4] test(strix): cover GitHub Models OpenAI fallback --- .github/workflows/strix.yml | 2 +- scripts/ci/test_strix_quick_gate.sh | 68 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 8eb5be301f..9b30488388 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -674,7 +674,7 @@ jobs: echo "LLM_API_BASE_FILE=$llm_api_base_file" >> "$GITHUB_ENV" - name: Prepare GitHub Models fallback credentials - if: steps.gate.outputs.provider_mode == 'openai_direct' || steps.gate.outputs.provider_mode == 'openrouter' || steps.gate.outputs.provider_mode == 'nvidia_nim' + if: steps.gate.outputs.provider_mode == 'github_models' || steps.gate.outputs.provider_mode == 'openai_direct' || steps.gate.outputs.provider_mode == 'openrouter' || steps.gate.outputs.provider_mode == 'nvidia_nim' env: GITHUB_MODELS_FALLBACK_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} OPENAI_FALLBACK_KEY: ${{ secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY }} diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 173ec13df1..571bf4dd9a 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -365,6 +365,7 @@ assert_strix_workflow_pr_trigger_hardened() { assert_file_contains "$workflow_file" "steps.gate.outputs.provider_mode == 'nvidia_nim' && 'nvidia_nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 openai-direct/gpt-5.6-luna'" "strix workflow gives NVIDIA NIM scans contracted fallbacks" assert_file_not_contains "$workflow_file" "STRIX_FALLBACK_MODELS: \${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/openai/o3" "strix workflow fallback list must not depend on GitHub Models, which is in platform-wide retirement" assert_file_contains "$workflow_file" "Prepare GitHub Models fallback credentials" "strix workflow provisions GitHub Models fallback credentials for direct-OpenAI scans" + assert_file_contains "$workflow_file" "steps.gate.outputs.provider_mode == 'github_models' || steps.gate.outputs.provider_mode == 'openai_direct'" "strix workflow provisions direct-OpenAI fallback credentials for GitHub Models scans" assert_file_contains "$workflow_file" "STRIX_OPENAI_FALLBACK_API_BASE_FILE" "strix workflow routes direct-OpenAI fallbacks through a trusted API base file" assert_file_contains "$workflow_file" "https://api.openai.com/v1" "strix workflow uses the OpenAI platform endpoint for direct fallbacks" assert_file_contains "$GATE_SCRIPT" "STRIX_GITHUB_MODELS_KEY_FILE" "strix gate reads the optional GitHub Models fallback key file" @@ -3447,6 +3448,30 @@ REPORT ;; esac ;; + github-models-openai-direct-fallback-success) + case "${STRIX_LLM:-}" in + openai/gpt-5) + if [ "${LLM_API_KEY:-}" != "dummy" ] || [ "${LLM_API_BASE:-}" != "https://models.github.ai/inference" ]; then + echo "unexpected GitHub Models primary credentials or endpoint" >&2 + exit 20 + fi + echo "openai.RateLimitError: Error code: 429 - GitHub Models quota exhausted" + exit 1 + ;; + openai/gpt-5.6-luna) + if [ "${LLM_API_KEY:-}" != "openai-fallback-key" ] || [ "${LLM_API_BASE:-}" != "https://api.openai.com/v1" ]; then + echo "unexpected GitHub-to-OpenAI fallback credentials or endpoint" >&2 + exit 21 + fi + echo "scan ok after GitHub-to-OpenAI fallback" + exit 0 + ;; + *) + echo "unexpected GitHub-to-OpenAI fallback model ${STRIX_LLM:-}" >&2 + exit 22 + ;; + esac + ;; vertex-all-notfound) echo "Error: litellm.NotFoundError: Vertex_aiException - x" echo '"status": "NOT_FOUND"' @@ -5680,6 +5705,12 @@ PY env_cmd+=(STRIX_OPENAI_FALLBACK_KEY_FILE="$tmp_dir/openai_fallback_key.txt") env_cmd+=(STRIX_OPENAI_FALLBACK_API_BASE_FILE="$tmp_dir/openai_fallback_api_base.txt") fi + if [ "$scenario" = "github-models-openai-direct-fallback-success" ]; then + printf '%s' 'openai-fallback-key' >"$tmp_dir/openai_fallback_key.txt" + printf '%s' 'https://api.openai.com/v1' >"$tmp_dir/openai_fallback_api_base.txt" + env_cmd+=(STRIX_OPENAI_FALLBACK_KEY_FILE="$tmp_dir/openai_fallback_key.txt") + env_cmd+=(STRIX_OPENAI_FALLBACK_API_BASE_FILE="$tmp_dir/openai_fallback_api_base.txt") + fi if [ "$min_fail_severity" = "__UNSET__" ]; then local next_env_cmd=() local env_pair @@ -5965,6 +5996,37 @@ run_nvidia_nim_openai_direct_fallback_case() { "openai-direct/gpt-5.6-luna" } +run_github_models_openai_direct_fallback_case() { + run_gate_case_allow_provider_signal "github-models-openai-direct-fallback-success" \ + "openai/gpt-5" \ + "" \ + "0" \ + "REGEX:Strix quick scan succeeded with fallback model 'openai-direct/gpt-5.6-luna' in [0-9]+s\\." \ + "2" \ + "openai/gpt-5|openai/gpt-5.6-luna" \ + "https://models.github.ai/inference|https://api.openai.com/v1" \ + "openai" \ + "https://models.github.ai/inference" \ + "" \ + "0" \ + "CRITICAL" \ + "0" \ + "" \ + "" \ + "1200" \ + "0" \ + "" \ + "" \ + "" \ + "" \ + "0" \ + "" \ + "" \ + "" \ + "__SAME_AS_FALLBACK_MODELS__" \ + "openai-direct/gpt-5.6-luna" +} + run_github_models_http410_case() { local scenario="$1" local expected_exit="$2" @@ -6190,6 +6252,9 @@ run_filtered_gate_case_if_requested() { nvidia-nim-openai-direct-fallback-success) run_nvidia_nim_openai_direct_fallback_case ;; + github-models-openai-direct-fallback-success) + run_github_models_openai_direct_fallback_case + ;; gemini-timeout-fallback-success) run_gate_case_allow_provider_signal "gemini-timeout-fallback-success" \ "gemini/timeout-fallback-primary" \ @@ -12584,6 +12649,9 @@ run_gate_case "openai-direct-quota-github-models-fallback-success" \ # the OpenAI endpoint and its own credential, not the primary provider input. run_nvidia_nim_openai_direct_fallback_case +# GitHub Models primary scans use the same direct-OpenAI fallback contract. +run_github_models_openai_direct_fallback_case + run_gate_case "github-models-fallback-success-deepseek-v3" \ "vertex_ai/missing-primary" \ "github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324" \