Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion scripts/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def fetch_pull_request_diff(
url,
token,
payload=None,
accept="application/vnd.github.diff",
accept="application/vnd.github.v3.diff",
**_retry_kwargs(retry_attempts, retry_backoff),
)
return response.text
Expand Down
2 changes: 1 addition & 1 deletion templates/consumer-repo/scripts/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def fetch_pull_request_diff(
url,
token,
payload=None,
accept="application/vnd.github.diff",
accept="application/vnd.github.v3.diff",
**_retry_kwargs(retry_attempts, retry_backoff),
)
return response.text
Expand Down
25 changes: 12 additions & 13 deletions templates/consumer-repo/tools/ci_failure_triage.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,20 +297,19 @@ def _get_llm_client() -> tuple[_LLMClient, str] | None:

if github_token:
model = configured_model_for_provider("github-models")
if not model:
return None
return (
cast(
_LLMClient,
ChatOpenAI(
model=model,
base_url=GITHUB_MODELS_BASE_URL,
api_key=cast(Any, github_token),
temperature=0.1,
if model:
return (
cast(
_LLMClient,
ChatOpenAI(
model=model,
base_url=GITHUB_MODELS_BASE_URL,
api_key=cast(Any, github_token),
temperature=0.1,
),
),
),
"github-models",
)
"github-models",
)
if openai_token is None:
return None
model = configured_model_for_provider("openai")
Expand Down
15 changes: 9 additions & 6 deletions templates/consumer-repo/tools/llm_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ def load_slot_config(*, github_default_model: str = "") -> list[SlotDefinition]:
select_model_for_profile(provider=provider, profile=profile, registry=registry)
or ""
)
if provider and explicit_model and explicit_model != model:
explicit_entry = registry_entry_for(provider, explicit_model, registry=registry)
if provider and explicit_model and not configured_profile and explicit_entry and not (
explicit_entry.blocked or explicit_entry.lifecycle != "current"
):
model = explicit_model
elif provider and explicit_model and explicit_model != model:
logger.warning(
"Ignoring slot model pin %s/%s; reviewed %s selection is %s",
provider,
Expand All @@ -357,11 +362,9 @@ def load_slot_config(*, github_default_model: str = "") -> list[SlotDefinition]:
continue
name = str(entry.get("name") or f"slot{idx}").strip() or f"slot{idx}"
slots.append(SlotDefinition(name=name, provider=provider, model=model))
# A syntactically valid config can still yield no usable entries (for
# example, every profile was retired or every model was blocked). That is
# equivalent to no usable slot configuration; retain the reviewed defaults
# instead of silently disabling every provider.
return slots or fallback_slots
# A present slot file is an allowlist. If every configured slot is
# unusable, fail closed instead of broadening execution to default providers.
return slots if slot_entries else fallback_slots
Comment thread
stranske marked this conversation as resolved.
Outdated


def apply_slot_env_overrides(
Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/test_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def fake_response(*args: object, **kwargs: object) -> object:

assert api_client.fetch_pull_request("owner/repo", 7, "tok") == {"title": "PR"}
assert api_client.fetch_pull_request_diff("owner/repo", 7, "tok") == response.text
assert calls == [{"payload": None, "accept": "application/vnd.github.diff"}]
assert calls == [{"payload": None, "accept": "application/vnd.github.v3.diff"}]


def test_fetch_pull_request_rejects_nonobject_payload(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down
17 changes: 8 additions & 9 deletions tests/tools/test_llm_registry_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,18 @@ def test_registry_decision_update_changes_slot_without_slot_edit(
assert "model" not in json.loads(slots_path.read_text())["slots"][0]


def test_legacy_slot_pin_cannot_override_reviewed_selection(
def test_legacy_slot_pin_is_honored_when_current_and_unblocked(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
registry_path = tmp_path / "registry.json"
slots_path = tmp_path / "slots.json"
_write_registry(registry_path)
_write_slots(slots_path, model="model-frontier")
_write_slots(slots_path, model="model-frontier", profile="")
monkeypatch.setenv(registry.ENV_MODEL_REGISTRY_CONFIG, str(registry_path))
monkeypatch.setenv(registry.ENV_SLOT_CONFIG, str(slots_path))

assert registry.load_slot_config()[0].model == "model-balanced"
assert registry.configured_model_for_provider("openai") == "model-balanced"
assert registry.load_slot_config()[0].model == "model-frontier"
assert registry.configured_model_for_provider("openai") == "model-frontier"


def test_unknown_explicit_profile_fails_closed(
Expand All @@ -126,13 +126,12 @@ def test_unknown_explicit_profile_fails_closed(
monkeypatch.setenv(registry.ENV_MODEL_REGISTRY_CONFIG, str(registry_path))
monkeypatch.setenv(registry.ENV_SLOT_CONFIG, str(slots_path))

# An unresolved slot profile must not disable the provider-level reviewed
# selection used as the safe fallback.
assert registry.load_slot_config()[0].model == "model-balanced"
# A present slot config is an allowlist; an unresolved profile fails closed.
assert registry.load_slot_config() == []
assert registry.configured_model_for_provider("openai") == "model-balanced"


def test_all_unusable_slot_entries_fall_back_to_reviewed_defaults(
def test_all_unusable_slot_entries_fail_closed(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
registry_path = tmp_path / "registry.json"
Expand All @@ -142,7 +141,7 @@ def test_all_unusable_slot_entries_fall_back_to_reviewed_defaults(
monkeypatch.setenv(registry.ENV_MODEL_REGISTRY_CONFIG, str(registry_path))
monkeypatch.setenv(registry.ENV_SLOT_CONFIG, str(slots_path))

assert registry.load_slot_config()[0].model == "model-balanced"
assert registry.load_slot_config() == []


def test_noncurrent_selected_model_fails_closed(
Expand Down
25 changes: 12 additions & 13 deletions tools/ci_failure_triage.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,20 +297,19 @@ def _get_llm_client() -> tuple[_LLMClient, str] | None:

if github_token:
model = configured_model_for_provider("github-models")
if not model:
return None
return (
cast(
_LLMClient,
ChatOpenAI(
model=model,
base_url=GITHUB_MODELS_BASE_URL,
api_key=cast(Any, github_token),
temperature=0.1,
if model:
Comment thread
stranske marked this conversation as resolved.
return (
cast(
_LLMClient,
ChatOpenAI(
model=model,
base_url=GITHUB_MODELS_BASE_URL,
api_key=cast(Any, github_token),
temperature=0.1,
),
),
),
"github-models",
)
"github-models",
)
if openai_token is None:
return None
model = configured_model_for_provider("openai")
Expand Down
2 changes: 1 addition & 1 deletion tools/evaluate_model_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def evaluate_benchmark(payload: dict[str, Any], policy: dict[str, Any]) -> dict[
noninferiority_margin = float(gates["paired_success_noninferiority_margin"])

for candidate in candidates:
model_id = str(candidate["model_id"])
model_id = str(candidate["model_id"]).strip()
metrics = metrics_by_model[model_id]
gate_results = {
"minimum_adjudicated_cases": metrics["sample_count"] >= minimum_cases,
Expand Down
15 changes: 9 additions & 6 deletions tools/llm_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ def load_slot_config(*, github_default_model: str = "") -> list[SlotDefinition]:
select_model_for_profile(provider=provider, profile=profile, registry=registry)
or ""
)
if provider and explicit_model and explicit_model != model:
explicit_entry = registry_entry_for(provider, explicit_model, registry=registry)
if provider and explicit_model and not configured_profile and explicit_entry and not (
explicit_entry.blocked or explicit_entry.lifecycle != "current"
):
model = explicit_model
Comment thread
stranske marked this conversation as resolved.
Outdated
elif provider and explicit_model and explicit_model != model:
logger.warning(
"Ignoring slot model pin %s/%s; reviewed %s selection is %s",
provider,
Expand All @@ -357,11 +362,9 @@ def load_slot_config(*, github_default_model: str = "") -> list[SlotDefinition]:
continue
name = str(entry.get("name") or f"slot{idx}").strip() or f"slot{idx}"
slots.append(SlotDefinition(name=name, provider=provider, model=model))
# A syntactically valid config can still yield no usable entries (for
# example, every profile was retired or every model was blocked). That is
# equivalent to no usable slot configuration; retain the reviewed defaults
# instead of silently disabling every provider.
return slots or fallback_slots
# A present slot file is an allowlist. If every configured slot is
# unusable, fail closed instead of broadening execution to default providers.
return slots if slot_entries else fallback_slots
Comment thread
stranske marked this conversation as resolved.
Outdated


def apply_slot_env_overrides(
Expand Down
Loading