Skip to content
Closed
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Recognize the hyphenated `openai-direct/` fallback alias (pinned verbatim by
protected main's own trusted `strix_required_workflow_smoke.sh`, so the
`STRIX_FALLBACK_MODELS` string itself cannot change) in
`child_model_for_api_base`, alongside the existing underscored
`openai_direct/` form. Previously the hyphenated alias passed through
unrecognized and unrewritten, so a NIM-exhaustion fallback to
`openai-direct/gpt-5.6-luna` reached LiteLLM as a literal, unrecognized
provider string (`litellm.BadRequestError: LLM Provider NOT provided`)
instead of the intended `openai/gpt-5.6-luna`, observed after NVIDIA NIM
rate-limited both the primary and first fallback model in three
consecutive Strix runs.
- Publish only the sanitized cumulative Strix report tree, avoiding a later
copy of relative scanner output that could reintroduce known internal warning
text into uploaded security evidence.
Expand Down
4 changes: 2 additions & 2 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2448,8 +2448,8 @@ child_model_for_api_base() {
fi

case "$model" in
openai_direct/*)
printf 'openai/%s\n' "${model#openai_direct/}"
openai_direct/* | openai-direct/*)
printf 'openai/%s\n' "${model#*/}"
Comment on lines +2451 to +2452

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Prefix strip change is equivalent

Replacing ${model#openai_direct/} with ${model#*/} is safe: the case guard only matches openai_direct/* or openai-direct/*, so the first / always ends the provider prefix. normalize_model (scripts/ci/strix_model_utils.sh:108-111) leaves slash-containing fallback candidates unchanged, so both alias spellings reach this arm and resolve to openai/.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

return 0
;;
esac
Expand Down
51 changes: 51 additions & 0 deletions tests/test_strix_nvidia_nim_not_found_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,34 @@ def _classifies_as_nvidia_not_found(log_text: str) -> bool:
return completed.returncode == 0


def _child_model_for_api_base(model: str, llm_api_base_value: str) -> str:
"""Execute the production model-alias normalizer against one input pair."""

gate_source = STRIX_GATE.read_text(encoding="utf-8")
function_source = "\n".join(
_function_block(gate_source, name)
for name in (
"is_github_models_api_base",
"is_github_models_model",
"child_model_for_api_base",
)
)
script = "\n".join(
(
"set -euo pipefail",
function_source,
'child_model_for_api_base "$1" "$2"',
)
)
completed = subprocess.run(
["bash", "-c", script, "strix-normalizer", model, llm_api_base_value],
check=True,
capture_output=True,
text=True,
)
return completed.stdout.strip()


def _workflow_signal_pattern(workflow: str, variable_name: str) -> str:
"""Extract one single-quoted POSIX ERE assigned in the Strix workflow."""

Expand Down Expand Up @@ -213,6 +241,29 @@ def test_workflow_uses_available_free_first_nvidia_plan(self) -> None:
)[0]
self.assertNotIn(RETIRED_PRIMARY_MODEL, default_gate)

def test_gate_normalizes_hyphenated_openai_direct_fallback_alias(self) -> None:
"""Route the NIM-exhaustion fallback alias to a real LiteLLM provider.

`STRIX_FALLBACK_MODELS`' NVIDIA NIM entry ends in the hyphenated
`openai-direct/gpt-5.6-luna` alias (the workflow's user-facing input
spelling, also pinned verbatim by protected main's own trusted
`strix_required_workflow_smoke.sh`, so this exact string cannot
change). The gate must still resolve it to LiteLLM's `openai/`
provider -- the same target the underscored `openai_direct/` alias
already reaches -- or NVIDIA NIM rate-limiting the primary and first
fallback model leaves the run one hop from
`litellm.BadRequestError: LLM Provider NOT provided`.
"""

self.assertEqual(
_child_model_for_api_base("openai-direct/gpt-5.6-luna", ""),
"openai/gpt-5.6-luna",
)
self.assertEqual(
_child_model_for_api_base("openai_direct/gpt-5.6-luna", ""),
"openai/gpt-5.6-luna",
)

def test_outer_workflow_requires_litellm_context_for_nvidia_404(self) -> None:
"""Reject provider-like target text in the outer neutralization gate."""

Expand Down
Loading