From 6b72ee64f83e9ce8bc44638a3c0edda3f9eae4a1 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sat, 25 Jul 2026 21:28:28 +0530 Subject: [PATCH 01/11] fix(agent): coerce AgentPolicyConfig fields to string to resolve #169 --- .../src/inspect_robots_agent/policy.py | 11 +++++++++++ .../tests/test_policy_e2e.py | 17 +++++++++++++++++ tests/test_rerun_sink.py | 13 ++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 085a7cc62..1256a1a97 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -256,6 +256,17 @@ def __init__( env: dict[str, str] | None = None, pre_check: PreCheck | None = None, ) -> None: + if model is not None: + model = str(model) if model else "" + if base_url is not None: + base_url = str(base_url) if base_url else "" + if api_key_env is not None: + api_key_env = str(api_key_env) if api_key_env else "" + if effort is not None: + effort = str(effort) if effort else "" + if speed is not None: + speed = str(speed) if speed else "" + prior_learnings_path: str | None = None prior_learnings_text: str | None = None prior_learnings_sha256: str | None = None diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index d4df21d4e..edd5c7f34 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2519,3 +2519,20 @@ def test_chat_wire_usage_metadata_counts_calls_only(tmp_path: Path) -> None: ) assert sink.records[0].metadata["llm_usage"] == {"llm_calls": 2} + + +def test_non_string_param_coercion() -> None: + policy = LLMAgentPolicy( + model=42, # type: ignore[arg-type] + base_url=True, # type: ignore[arg-type] + api_key_env=False, # type: ignore[arg-type] + effort="minimal", + speed="fast", + wire="anthropic", + env={"False": "test-key"}, + ) + assert policy.config.model == "42" + assert policy.config.base_url == "True" + assert policy.config.api_key_env == "" + assert policy.config.effort == "minimal" + assert policy.config.speed == "fast" diff --git a/tests/test_rerun_sink.py b/tests/test_rerun_sink.py index 5bae1e666..11cb2457b 100644 --- a/tests/test_rerun_sink.py +++ b/tests/test_rerun_sink.py @@ -2,7 +2,6 @@ from __future__ import annotations -import importlib.util import socket import subprocess import sys @@ -32,7 +31,12 @@ from inspect_robots.task import Task from inspect_robots.types import Action, Observation, StepResult -_RERUN_INSTALLED = importlib.util.find_spec("rerun") is not None +try: + import rerun # noqa: F401 + + _RERUN_INSTALLED = True +except ImportError: + _RERUN_INSTALLED = False def _task() -> Task: @@ -1032,7 +1036,10 @@ def test_real_rerun_accepts_the_transcript_document_call() -> None: def test_real_rerun_process_exits_when_tcp_peer_never_reads() -> None: """The real SDK atexit path is bounded after a connected peer stops reading.""" - rr = pytest.importorskip("rerun") + if not _RERUN_INSTALLED: + pytest.skip("requires rerun-sdk") + import rerun as rr + if not hasattr(rr, "connect_grpc"): pytest.skip("pre-gRPC rerun-sdk cannot run the connect-mode wedge scenario") server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) From e7385def83f8d0eb8f2aba1f4d1ca8a3e3947fc4 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sun, 26 Jul 2026 13:34:27 +0530 Subject: [PATCH 02/11] fix(agent): comment param coercion, use dummy env in tests, and update CHANGELOG --- CHANGELOG.md | 1 + .../inspect-robots-agent/src/inspect_robots_agent/policy.py | 4 ++++ plugins/inspect-robots-agent/tests/test_policy_e2e.py | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b4beb6e9..85185a92c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,7 @@ All notable changes to this project are documented here. The format is based on regression (`eef_delta_pose` + `rot6d` already reached the displacement clamp path before #143/#144). `euler_xyz` and `axis_angle` deltas have no such problem and remain guardrail-conformant. +- **Coerce non-string agent policy config parameters to string** in `LLMAgentPolicy` constructor to prevent `AttributeError`s (e.g., when `model` is passed as an integer like 42) and ensure clean logging (#169). Falsy values are coerced to `""` to preserve API key and OpenRouter fallback logic. - **An explicit invalid `--max-action-delta` now fails fast instead of silently running with weaker guardrails** (#154). Non-finite or non-positive values were previously caught by `_build_guardrails`'s degrade-per-component path diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 1256a1a97..b75e35431 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -256,6 +256,10 @@ def __init__( env: dict[str, str] | None = None, pre_check: PreCheck | None = None, ) -> None: + # Coerce non-None parameters to str. We use `str(x) if x else ""` so that falsy values + # (like False or 0) coerce to the empty string "" instead of "False" or "0". + # For example, if api_key_env is False, coercing to "False" would cause env lookup + # to search for an env var named literally "False" and bypass default key/openrouter fallbacks. if model is not None: model = str(model) if model else "" if base_url is not None: diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index edd5c7f34..beb40cbec 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2529,7 +2529,7 @@ def test_non_string_param_coercion() -> None: effort="minimal", speed="fast", wire="anthropic", - env={"False": "test-key"}, + env={"DUMMY_KEY": "test-key"}, ) assert policy.config.model == "42" assert policy.config.base_url == "True" From 2eb84bd35ca7ab43d2f35e8515aee3c83daddc22 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sun, 26 Jul 2026 13:45:04 +0530 Subject: [PATCH 03/11] fix error --- plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index b75e35431..4d383ab9c 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -259,7 +259,7 @@ def __init__( # Coerce non-None parameters to str. We use `str(x) if x else ""` so that falsy values # (like False or 0) coerce to the empty string "" instead of "False" or "0". # For example, if api_key_env is False, coercing to "False" would cause env lookup - # to search for an env var named literally "False" and bypass default key/openrouter fallbacks. + # to search for an env var named literally "False" and bypass default key fallbacks. if model is not None: model = str(model) if model else "" if base_url is not None: From 67726a18df9b588ccb6cdd07e23d807c75151273 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Thu, 30 Jul 2026 07:32:05 +0530 Subject: [PATCH 04/11] fix(agent): reject non-string config options with ConfigError --- CHANGELOG.md | 6 ++- .../src/inspect_robots_agent/policy.py | 29 ++++++------ .../tests/test_policy_e2e.py | 44 +++++++++++++------ 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85185a92c..ea5bed45f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,7 +211,11 @@ All notable changes to this project are documented here. The format is based on regression (`eef_delta_pose` + `rot6d` already reached the displacement clamp path before #143/#144). `euler_xyz` and `axis_angle` deltas have no such problem and remain guardrail-conformant. -- **Coerce non-string agent policy config parameters to string** in `LLMAgentPolicy` constructor to prevent `AttributeError`s (e.g., when `model` is passed as an integer like 42) and ensure clean logging (#169). Falsy values are coerced to `""` to preserve API key and OpenRouter fallback logic. +- **Agent policy configuration parameters now strictly reject non-strings** + in `LLMAgentPolicy` constructor, raising a guided `ConfigError` (#169). This + prevents unquoted CLI values (e.g., `-P model=42` or `-P api_key_env=false`) + from causing downstream errors or incorrect fallback logic, prompting the + user to pass quoted strings instead. - **An explicit invalid `--max-action-delta` now fails fast instead of silently running with weaker guardrails** (#154). Non-finite or non-positive values were previously caught by `_build_guardrails`'s degrade-per-component path diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 4d383ab9c..3203cdb1b 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -256,20 +256,21 @@ def __init__( env: dict[str, str] | None = None, pre_check: PreCheck | None = None, ) -> None: - # Coerce non-None parameters to str. We use `str(x) if x else ""` so that falsy values - # (like False or 0) coerce to the empty string "" instead of "False" or "0". - # For example, if api_key_env is False, coercing to "False" would cause env lookup - # to search for an env var named literally "False" and bypass default key fallbacks. - if model is not None: - model = str(model) if model else "" - if base_url is not None: - base_url = str(base_url) if base_url else "" - if api_key_env is not None: - api_key_env = str(api_key_env) if api_key_env else "" - if effort is not None: - effort = str(effort) if effort else "" - if speed is not None: - speed = str(speed) if speed else "" + # Reject non-strings with a guided ConfigError to prevent unquoted CLI + # values (e.g. -P model=42) from causing downstream errors or silent bypasses. + for name, val in [ + ("model", model), + ("base_url", base_url), + ("api_key_env", api_key_env), + ("effort", effort), + ("speed", speed), + ]: + if val is not None and not isinstance(val, str): + raise ConfigError( + f"{name} must be a string, got {val!r}.\n" + f"fix: the -P parser coerces unquoted values; pass " + f"-P '{name}=\"value\"'" + ) prior_learnings_path: str | None = None prior_learnings_text: str | None = None diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index beb40cbec..2bfc33453 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2522,17 +2522,33 @@ def test_chat_wire_usage_metadata_counts_calls_only(tmp_path: Path) -> None: def test_non_string_param_coercion() -> None: - policy = LLMAgentPolicy( - model=42, # type: ignore[arg-type] - base_url=True, # type: ignore[arg-type] - api_key_env=False, # type: ignore[arg-type] - effort="minimal", - speed="fast", - wire="anthropic", - env={"DUMMY_KEY": "test-key"}, - ) - assert policy.config.model == "42" - assert policy.config.base_url == "True" - assert policy.config.api_key_env == "" - assert policy.config.effort == "minimal" - assert policy.config.speed == "fast" + for param, val in [ + ("model", 42), + ("model", 0), + ("base_url", True), + ("base_url", False), + ("api_key_env", True), + ("api_key_env", False), + ("effort", 123), + ("effort", False), + ("speed", False), + ]: + kwargs = { + "model": "test-model", + "base_url": "http://localhost:8000", + "api_key_env": "TEST_KEY", + "effort": "low", + "speed": None, + "wire": "chat", + } + kwargs[param] = val # type: ignore[misc] + + # speed requires wire='anthropic' + if param == "speed": + kwargs["wire"] = "anthropic" + + with pytest.raises(ConfigError) as exc_info: + LLMAgentPolicy(**kwargs) + + assert f"{param} must be a string, got {val!r}." in str(exc_info.value) + assert f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" in str(exc_info.value) From 77f4fc22c2218ed386fb4636b16973ad67b4b3f1 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Thu, 30 Jul 2026 07:35:43 +0530 Subject: [PATCH 05/11] fix lint check --- plugins/inspect-robots-agent/tests/test_policy_e2e.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index 2bfc33453..c8f65369d 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2551,4 +2551,5 @@ def test_non_string_param_coercion() -> None: LLMAgentPolicy(**kwargs) assert f"{param} must be a string, got {val!r}." in str(exc_info.value) - assert f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" in str(exc_info.value) + expected_fix = f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" + assert expected_fix in str(exc_info.value) From e44c73f5ecd37a786bb4e7453a58f7e51c16f728 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Thu, 30 Jul 2026 07:49:40 +0530 Subject: [PATCH 06/11] fix(agent): update test parameterization and types for strict string validation --- plugins/inspect-robots-agent/tests/test_anthropic.py | 6 +++--- plugins/inspect-robots-agent/tests/test_policy_e2e.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/inspect-robots-agent/tests/test_anthropic.py b/plugins/inspect-robots-agent/tests/test_anthropic.py index 766d28db2..c3ec285d5 100644 --- a/plugins/inspect-robots-agent/tests/test_anthropic.py +++ b/plugins/inspect-robots-agent/tests/test_anthropic.py @@ -1099,9 +1099,9 @@ def test_variant_strip_keeps_fine_tune_colons() -> None: ) -@pytest.mark.parametrize("api_key_env", [None, "", False, 0, 0.0]) +@pytest.mark.parametrize("api_key_env", [None, ""]) def test_falsy_api_key_env_does_not_send_the_openrouter_key_to_a_gateway( - api_key_env: object, + api_key_env: str | None, ) -> None: # '-P api_key_env=' parses to '', and 'false'/'0' to other falsy values, # all of which resolve_provider treats as unset and answers with @@ -1112,7 +1112,7 @@ def test_falsy_api_key_env_does_not_send_the_openrouter_key_to_a_gateway( model="claude-opus-5", wire="anthropic", base_url="https://gw.example/v1", - api_key_env=api_key_env, # type: ignore[arg-type] + api_key_env=api_key_env, transport=httpx.MockTransport(handler), env={"ANTHROPIC_API_KEY": "sk-ant", "OPENROUTER_API_KEY": "sk-or"}, ) diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index c8f65369d..d4be54191 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2533,7 +2533,7 @@ def test_non_string_param_coercion() -> None: ("effort", False), ("speed", False), ]: - kwargs = { + kwargs: dict[str, Any] = { "model": "test-model", "base_url": "http://localhost:8000", "api_key_env": "TEST_KEY", @@ -2541,7 +2541,7 @@ def test_non_string_param_coercion() -> None: "speed": None, "wire": "chat", } - kwargs[param] = val # type: ignore[misc] + kwargs[param] = val # speed requires wire='anthropic' if param == "speed": From 7556f15fc09f4da771ae1575a46201e98a62e150 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Mon, 3 Aug 2026 00:58:24 +0530 Subject: [PATCH 07/11] fix(agent): skip _UNSET sentinel in config validation and clean up test nits --- .../src/inspect_robots_agent/policy.py | 2 ++ plugins/inspect-robots-agent/tests/test_anthropic.py | 7 +++---- plugins/inspect-robots-agent/tests/test_policy_e2e.py | 7 ++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index c2392478e..f6004c9b6 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -285,6 +285,8 @@ def __init__( ("effort", effort), ("speed", speed), ]: + if val is _UNSET: + continue if val is not None and not isinstance(val, str): raise ConfigError( f"{name} must be a string, got {val!r}.\n" diff --git a/plugins/inspect-robots-agent/tests/test_anthropic.py b/plugins/inspect-robots-agent/tests/test_anthropic.py index c3ec285d5..47ffa484b 100644 --- a/plugins/inspect-robots-agent/tests/test_anthropic.py +++ b/plugins/inspect-robots-agent/tests/test_anthropic.py @@ -1103,10 +1103,9 @@ def test_variant_strip_keeps_fine_tune_colons() -> None: def test_falsy_api_key_env_does_not_send_the_openrouter_key_to_a_gateway( api_key_env: str | None, ) -> None: - # '-P api_key_env=' parses to '', and 'false'/'0' to other falsy values, - # all of which resolve_provider treats as unset and answers with - # $OPENROUTER_API_KEY. An `is None` test would hand a third-party gateway - # the OpenRouter secret. + # '-P api_key_env=' parses to '', which resolve_provider treats as unset + # and answers with $OPENROUTER_API_KEY. An `is None` test would hand a + # third-party gateway the OpenRouter secret. seen, handler = _capture(_anthropic_response(_text("ok"), stop_reason="end_turn")) policy = LLMAgentPolicy( model="claude-opus-5", diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index ef16300e0..23c2507fd 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2539,7 +2539,7 @@ def test_chat_wire_usage_metadata_counts_calls_only(tmp_path: Path) -> None: assert sink.records[0].metadata["llm_usage"] == {"llm_calls": 2} -def test_non_string_param_coercion() -> None: +def test_non_string_params_rejected() -> None: for param, val in [ ("model", 42), ("model", 0), @@ -2571,3 +2571,8 @@ def test_non_string_param_coercion() -> None: assert f"{param} must be a string, got {val!r}." in str(exc_info.value) expected_fix = f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" assert expected_fix in str(exc_info.value) + + +def test_policy_config_defaults() -> None: + policy = LLMAgentPolicy(model="anthropic/claude-3-5-sonnet", base_url="http://localhost:8000") + assert policy._pre_check is None From 155dd9e7187aa57666e7e398ef34244651dbbf9d Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Wed, 5 Aug 2026 08:19:53 +0530 Subject: [PATCH 08/11] chore: remove unused importlib.util in test_rerun_sink.py --- tests/test_rerun_sink.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_rerun_sink.py b/tests/test_rerun_sink.py index db65182dc..a1ff5ce16 100644 --- a/tests/test_rerun_sink.py +++ b/tests/test_rerun_sink.py @@ -2,7 +2,6 @@ from __future__ import annotations -import importlib.util import inspect import socket import subprocess From d09876e858d8350af94dd2d59e8924b04c80e2f4 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sat, 8 Aug 2026 15:37:49 +0530 Subject: [PATCH 09/11] fix(agent): use isinstance for _Unset sentinel check --- plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 0b94f6777..2ddcb55ff 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -342,7 +342,7 @@ def __init__( ("effort", effort), ("speed", speed), ]: - if val is _UNSET: + if isinstance(val, _Unset): continue if val is not None and not isinstance(val, str): raise ConfigError( From ff3d5c0d260b5ce0ced998cda22a03caf29af23d Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sat, 8 Aug 2026 16:06:13 +0530 Subject: [PATCH 10/11] fix(agent): remove effort from strict string validation loop to support fractional effort --- plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py | 1 - plugins/inspect-robots-agent/tests/test_policy_e2e.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 2ddcb55ff..821f89987 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -339,7 +339,6 @@ def __init__( ("model", model), ("base_url", base_url), ("api_key_env", api_key_env), - ("effort", effort), ("speed", speed), ]: if isinstance(val, _Unset): diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index b1eca0e73..34b1d752a 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2801,8 +2801,6 @@ def test_non_string_params_rejected() -> None: ("base_url", False), ("api_key_env", True), ("api_key_env", False), - ("effort", 123), - ("effort", False), ("speed", False), ]: kwargs: dict[str, Any] = { From 75a451ce09a321261b5ecfe22f0b012c658ee354 Mon Sep 17 00:00:00 2001 From: Amresh-01 Date: Sun, 9 Aug 2026 10:53:51 +0530 Subject: [PATCH 11/11] fix(agent): address PR review comments, remove Unset branch, and parameterized tests --- .../src/inspect_robots_agent/policy.py | 2 - .../tests/test_policy_e2e.py | 48 +++++++++---------- tests/test_rerun_sink.py | 12 ++--- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py index 821f89987..26f4ab40f 100644 --- a/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py +++ b/plugins/inspect-robots-agent/src/inspect_robots_agent/policy.py @@ -341,8 +341,6 @@ def __init__( ("api_key_env", api_key_env), ("speed", speed), ]: - if isinstance(val, _Unset): - continue if val is not None and not isinstance(val, str): raise ConfigError( f"{name} must be a string, got {val!r}.\n" diff --git a/plugins/inspect-robots-agent/tests/test_policy_e2e.py b/plugins/inspect-robots-agent/tests/test_policy_e2e.py index 34b1d752a..5a0a3c398 100644 --- a/plugins/inspect-robots-agent/tests/test_policy_e2e.py +++ b/plugins/inspect-robots-agent/tests/test_policy_e2e.py @@ -2793,8 +2793,9 @@ def test_chat_wire_usage_metadata_counts_calls_only(tmp_path: Path) -> None: assert sink.records[0].metadata["llm_usage"] == {"llm_calls": 2} -def test_non_string_params_rejected() -> None: - for param, val in [ +@pytest.mark.parametrize( + "param, val", + [ ("model", 42), ("model", 0), ("base_url", True), @@ -2802,29 +2803,26 @@ def test_non_string_params_rejected() -> None: ("api_key_env", True), ("api_key_env", False), ("speed", False), - ]: - kwargs: dict[str, Any] = { - "model": "test-model", - "base_url": "http://localhost:8000", - "api_key_env": "TEST_KEY", - "effort": "low", - "speed": None, - "wire": "chat", - } - kwargs[param] = val - - # speed requires wire='anthropic' - if param == "speed": - kwargs["wire"] = "anthropic" - - with pytest.raises(ConfigError) as exc_info: - LLMAgentPolicy(**kwargs) + ], +) +def test_non_string_params_rejected(param: str, val: Any) -> None: + kwargs: dict[str, Any] = { + "model": "test-model", + "base_url": "http://localhost:8000", + "api_key_env": "TEST_KEY", + "effort": "low", + "speed": None, + "wire": "chat", + } + kwargs[param] = val - assert f"{param} must be a string, got {val!r}." in str(exc_info.value) - expected_fix = f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" - assert expected_fix in str(exc_info.value) + # speed requires wire='anthropic' + if param == "speed": + kwargs["wire"] = "anthropic" + with pytest.raises(ConfigError) as exc_info: + LLMAgentPolicy(**kwargs) -def test_policy_config_defaults() -> None: - policy = LLMAgentPolicy(model="anthropic/claude-3-5-sonnet", base_url="http://localhost:8000") - assert policy._pre_check is None + assert f"{param} must be a string, got {val!r}." in str(exc_info.value) + expected_fix = f"fix: the -P parser coerces unquoted values; pass -P '{param}=\"value\"'" + assert expected_fix in str(exc_info.value) diff --git a/tests/test_rerun_sink.py b/tests/test_rerun_sink.py index 790f12683..b106a7289 100644 --- a/tests/test_rerun_sink.py +++ b/tests/test_rerun_sink.py @@ -2,6 +2,7 @@ from __future__ import annotations +import importlib.util import inspect import re import socket @@ -43,12 +44,7 @@ from inspect_robots.task import Task from inspect_robots.types import Action, Observation, StepResult -try: - import rerun # noqa: F401 - - _RERUN_INSTALLED = True -except ImportError: - _RERUN_INSTALLED = False +_RERUN_INSTALLED = importlib.util.find_spec("rerun") is not None def _task() -> Task: @@ -1846,9 +1842,7 @@ def test_real_rerun_accepts_the_blueprint(tmp_path: Path) -> None: def test_real_rerun_process_exits_when_tcp_peer_never_reads() -> None: """The real SDK atexit path is bounded after a connected peer stops reading.""" - if not _RERUN_INSTALLED: - pytest.skip("requires rerun-sdk") - import rerun as rr + rr = pytest.importorskip("rerun") if not hasattr(rr, "connect_grpc"): pytest.skip("pre-gRPC rerun-sdk cannot run the connect-mode wedge scenario")