From ffa890ad733129c7928cc4326050f0440780b5e8 Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Mon, 2 Nov 2026 13:49:20 -0500 Subject: [PATCH 1/4] refactor(language): use the public participation interface --- README.md | 2 +- brainscore_language/__init__.py | 2 +- brainscore_language/compat/unified_adapter.py | 14 ++++++------- docs/UMI_MIGRATION.md | 2 +- tests/test_unified_adapter.py | 21 +++++++++++++++++-- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b1799fa4..60dfc5f9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ the `ArtificialSubject` interface can be scored on. > **UMI integration:** Existing domain plugins remain supported. Use > `BrainScoreModel` for extraction and task helpers, or implement -> `Subject.interact(session)` for a custom session integration. A native +> `Subject.participate(session)` for a custom session integration. A native > `Subject` does not require `process()`. See > [docs/UMI_MIGRATION.md](docs/UMI_MIGRATION.md). diff --git a/brainscore_language/__init__.py b/brainscore_language/__init__.py index 22e1357d..414ae182 100644 --- a/brainscore_language/__init__.py +++ b/brainscore_language/__init__.py @@ -83,7 +83,7 @@ def _run_score(model_identifier: str, benchmark_identifier: str, check_memory(model, benchmark) # A legacy benchmark calls digest_text and friends; it receives the permanent - # facade, which runs those calls as sessions through the subject's interact. + # facade, which runs those calls as sessions through the subject's participate. from brainscore_core.legacy_facade import subject_for_benchmark candidate = subject_for_benchmark(model, benchmark) diff --git a/brainscore_language/compat/unified_adapter.py b/brainscore_language/compat/unified_adapter.py index 15dbdd0f..67424649 100644 --- a/brainscore_language/compat/unified_adapter.py +++ b/brainscore_language/compat/unified_adapter.py @@ -14,9 +14,9 @@ from brainscore_core.session_view import DeclaredInputSession from brainscore_core.streaming import StreamEvent from brainscore_core.streaming_helpers import ( - _drain_stream_events, - _drive_behavior_session_via_process, - _drive_neural_session_via_process, + drain_session_inputs, + drive_behavior_session, + drive_neural_session, measurement_settings, ) @@ -169,7 +169,7 @@ def out_channels(self) -> Set[str]: return set(self._declared_out_channels) return super().out_channels | {'behavior'} - def interact(self, session) -> None: + def participate(self, session) -> None: """Run a session; a legacy-benchmark session replays the exact ``digest_text``. A session carrying ``legacy_text`` (from the legacy-benchmark facade) @@ -187,9 +187,9 @@ def interact(self, session) -> None: settings = measurement_settings(session) if 'legacy_text' not in settings: if channels == ('behavior',): - _drive_behavior_session_via_process(self, session) + drive_behavior_session(self, session) else: - _drive_neural_session_via_process(self, session) + drive_neural_session(self, session) return if context is not None and context.task_type != 'passive': self.start_task(context) @@ -197,7 +197,7 @@ def interact(self, session) -> None: if channel.startswith('neural:'): self.start_neural_recording(channel.split(':', 1)[1], settings.get('recording_type', 'fMRI')) - _drain_stream_events(session) + drain_session_inputs(session) result = self._legacy.digest_text(settings['legacy_text']) for channel in channels: key = 'neural' if channel.startswith('neural:') else 'behavior' diff --git a/docs/UMI_MIGRATION.md b/docs/UMI_MIGRATION.md index c1b5c6db..80fed2b9 100644 --- a/docs/UMI_MIGRATION.md +++ b/docs/UMI_MIGRATION.md @@ -6,7 +6,7 @@ | --- | --- | | Score an existing model | `brainscore.score(model_identifier, benchmark_identifier)` | | Reuse extraction and task helpers | `BrainScoreModel`, with `process`, task setup and recording methods | -| Define custom session behavior | `Subject.interact(session)`, with declared input/output channels | +| Define custom session behavior | `Subject.participate(session)`, with declared input/output channels | | Combine recording and interventions | `Experiment` with a compatible protocol and tools | `BrainScoreModel` is a `Subject` implementation. A native `Subject` does not need `process()` or a region mapping; a benchmark using those methods requires an implementation that provides them. diff --git a/tests/test_unified_adapter.py b/tests/test_unified_adapter.py index da1ed52d..241fcf64 100644 --- a/tests/test_unified_adapter.py +++ b/tests/test_unified_adapter.py @@ -279,7 +279,7 @@ def test_process_single_key_fallback(self): result = adapter.process(['text']) assert result is sentinel - def test_neural_response_interact_matches_legacy_process_exactly(self): + def test_neural_response_participate_matches_legacy_process_exactly(self): stimuli = _language_stimulus_set() expected = _language_neural_assembly() @@ -305,6 +305,23 @@ def test_neural_response_interact_matches_legacy_process_exactly(self): 'language_system', 'fMRI' ) + @pytest.mark.parametrize('channel', ['behavior', 'neural:language_system']) + def test_typed_session_refuses_variance_before_any_legacy_call(self, channel): + from brainscore_core.streaming import BufferedSession + legacy = _make_legacy_model( + region_layer_mapping={'language_system': 'language'}) + session = BufferedSession( + stimulus_set=_language_stimulus_set(), + task_context=TaskContext(task_type='next_word'), + requested_output_channels=(channel,)) + session.settings = {'require_variance': True} + with pytest.raises(NotImplementedError, match='trial-to-trial variance'): + LanguageModelAdapter(legacy).participate(session) + legacy.start_behavioral_task.assert_not_called() + legacy.start_neural_recording.assert_not_called() + legacy.digest_text.assert_not_called() + assert session.emitted == [] + class TestLanguageAdapterLegacyMethods: @@ -449,7 +466,7 @@ def test_load_model_wraps_legacy(self): # After load_model, identifier was overwritten to string 'test-gpt2' assert model.identifier == 'test-gpt2' - def test_load_model_wrapped_legacy_interact_scores(self): + def test_load_model_wrapped_legacy_participate_scores(self): import brainscore_language expected = _language_neural_assembly() legacy = _make_legacy_model( From 521a37a35522cb19c05e2e504b14e9e3d13cc836 Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Mon, 2 Nov 2026 13:53:43 -0500 Subject: [PATCH 2/4] ci(umi): test coordinated public extension API peers --- .github/workflows/umi-v2.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/umi-v2.yml b/.github/workflows/umi-v2.yml index 0e1423cb..cd05c9a2 100644 --- a/.github/workflows/umi-v2.yml +++ b/.github/workflows/umi-v2.yml @@ -11,9 +11,9 @@ jobs: coordinated: name: UMI v2 # Update the workflow and runner revision together. - uses: KartikP/brainscore-unified/.github/workflows/offline-tests.yml@529237ada8c243cab0ad67cb53abde350342f4ea + uses: KartikP/brainscore-unified/.github/workflows/offline-tests.yml@3b0ac4330adcd7250cb022897898d7aae3a3245c with: candidate: language candidate_repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} candidate_revision: ${{ github.event.pull_request.head.sha || github.sha }} - unified_revision: 529237ada8c243cab0ad67cb53abde350342f4ea + unified_revision: 3b0ac4330adcd7250cb022897898d7aae3a3245c From 851d303f567e9d26a5315c90f103962f10e12ed5 Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Mon, 2 Nov 2026 14:06:04 -0500 Subject: [PATCH 3/4] ci(umi): follow the refreshed v2 integration runner --- .github/workflows/umi-v2.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/umi-v2.yml b/.github/workflows/umi-v2.yml index cd05c9a2..0b05ec70 100644 --- a/.github/workflows/umi-v2.yml +++ b/.github/workflows/umi-v2.yml @@ -11,9 +11,9 @@ jobs: coordinated: name: UMI v2 # Update the workflow and runner revision together. - uses: KartikP/brainscore-unified/.github/workflows/offline-tests.yml@3b0ac4330adcd7250cb022897898d7aae3a3245c + uses: KartikP/brainscore-unified/.github/workflows/offline-tests.yml@5dc12f6654f5701b5135c7b288a3327ee704384e with: candidate: language candidate_repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} candidate_revision: ${{ github.event.pull_request.head.sha || github.sha }} - unified_revision: 3b0ac4330adcd7250cb022897898d7aae3a3245c + unified_revision: 5dc12f6654f5701b5135c7b288a3327ee704384e From 38a4da7e49629ea0d72cc2c845f093758131b2df Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Mon, 2 Nov 2026 14:18:42 -0500 Subject: [PATCH 4/4] fix(ci): align standalone core with the public extension API --- requirements-v2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-v2.txt b/requirements-v2.txt index 929e61fe..d3b5fb7d 100644 --- a/requirements-v2.txt +++ b/requirements-v2.txt @@ -1,2 +1,2 @@ # Unpublished v2 core, pinned for standalone CI and documentation builds. -brainscore-core @ git+https://github.com/brain-score/core.git@0fbb419ff0ba461e6b1e217c845e0fc61d8096a1 +brainscore-core @ git+https://github.com/brain-score/core.git@9e421f634699fdd4693fc6bc9e210dfb16109e7f