Skip to content

Commit beb5537

Browse files
fix(cli): reject nonfinite and out-of-range minimum scores (#196)
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> Co-authored-by: n-papaioannou <n.papaioannou@ime.life>
1 parent 0d24f5d commit beb5537

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

‎ifixai/cli/run.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ async def _probe_then_close(
333333
await _aclose_provider(provider)
334334

335335

336+
def _validate_min_score(ctx: click.Context, param: click.Parameter, value: float) -> float:
337+
if not 0 <= value <= 1:
338+
raise click.BadParameter("must be a finite number between 0 and 1", ctx=ctx, param=param)
339+
return value
340+
341+
336342
@click.command()
337343
@click.option(
338344
"--provider",
@@ -495,6 +501,7 @@ async def _probe_then_close(
495501
@click.option(
496502
"--min-score",
497503
type=float,
504+
callback=_validate_min_score,
498505
default=0.85,
499506
show_default=True,
500507
help="Minimum overall score; exit code 2 if below (default: 0.85 per ifixai spec).",

‎ifixai/tests/test_cli_keyless_providers.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,33 @@ def test_authenticated_provider_still_rejects_missing_key(
119119
assert result.exit_code == 1
120120
assert "No API key found" in result.output
121121
assert not (tmp_path / "ifixai-results").exists()
122+
123+
124+
@pytest.mark.parametrize("threshold", ["nan", "inf", "-inf", "-0.1", "1.1"])
125+
def test_invalid_minimum_score_fails_before_run(tmp_path, monkeypatch, threshold):
126+
monkeypatch.chdir(tmp_path)
127+
result = CliRunner().invoke(ifixai_cli, [
128+
"run", "--provider", "mock", "--fixture", "customer_support",
129+
"--test", "B01", "--test", "B08", "--test", "P01", "--test", "B25",
130+
"--eval-mode", "single", "--judge-provider", "mock",
131+
"--min-score", threshold, "--no-telemetry", "--no-parallel",
132+
])
133+
assert result.exit_code == 2, result.output
134+
assert "Invalid value for '--min-score'" in result.output
135+
assert not (tmp_path / "ifixai-results").exists()
136+
assert not (tmp_path / "runs").exists()
137+
138+
139+
@pytest.mark.parametrize("threshold,expected_exit", [("0", 0), ("1", 2)])
140+
def test_valid_minimum_score_preserves_offline_ci_gate(tmp_path, monkeypatch, threshold, expected_exit):
141+
monkeypatch.chdir(tmp_path)
142+
result = CliRunner().invoke(ifixai_cli, [
143+
"run", "--provider", "mock", "--fixture", "customer_support",
144+
"--test", "B01", "--test", "B08", "--test", "P01", "--test", "B25",
145+
"--eval-mode", "single", "--judge-provider", "mock",
146+
"--min-score", threshold, "--no-telemetry", "--no-parallel",
147+
])
148+
assert result.exit_code == expected_exit, result.output
149+
reports = list((tmp_path / "ifixai-results").glob("*.json"))
150+
assert len(reports) == 1
151+
assert 0 < json.loads(reports[0].read_text())["overall"]["score"] < 1

0 commit comments

Comments
 (0)