From 5f72c288cc07ded876cf54fd5d0d0582b91e0567 Mon Sep 17 00:00:00 2001 From: Adrian Hayler Date: Wed, 9 Sep 2026 16:35:11 +0200 Subject: [PATCH 1/2] Extract the shared core package --- .github/workflows/ci.yml | 7 +- README.md | 4 +- docs/adding-a-model.md | 12 +- docs/predictive-task.md | 6 +- docs/temporal-validation.md | 4 +- examples/tabpfn_rel_caching.py | 10 +- packages/relarena-core/LICENSE | 200 ++++++++++++++++++ packages/relarena-core/NOTICE | 16 ++ packages/relarena-core/README.md | 11 + packages/relarena-core/pyproject.toml | 27 +++ .../src/relarena_core}/__init__.py | 18 +- .../src/relarena_core}/cache.py | 0 .../src/relarena_core}/dataset.py | 4 +- .../src/relarena_core/discovery.py | 36 ++++ .../relarena_core}/featurization/__init__.py | 2 +- .../src/relarena_core}/featurization/cache.py | 2 +- .../relarena_core}/featurization/columns.py | 0 .../src/relarena_core}/featurization/dfs.py | 12 +- .../featurization/warm_cache.py | 6 +- .../src/relarena_core}/fingerprints.py | 0 .../src/relarena_core}/identity.py | 0 .../src/relarena_core}/metrics.py | 2 +- .../src/relarena_core}/model.py | 6 +- .../src/relarena_core}/predict_contract.py | 0 .../src/relarena_core}/py.typed | 0 .../src/relarena_core}/registry.py | 10 +- .../src/relarena_core}/results.py | 0 .../src/relarena_core}/search_space.py | 0 .../src/relarena_core}/selection.py | 4 +- .../src/relarena_core}/system.py | 8 +- .../src/relarena_core}/tasks.py | 0 .../src/relarena_core}/tfm.py | 4 +- .../src/relarena_core}/tuner.py | 16 +- .../src/relarena_core}/userdb/__init__.py | 6 +- .../src/relarena_core}/userdb/_schema.py | 2 +- .../userdb/database.schema.json | 0 .../src/relarena_core}/userdb/ingest.py | 2 +- .../src/relarena_core}/userdb/predict.py | 4 +- .../src/relarena_core}/userdb/query.py | 36 ++-- .../src/relarena_core}/userdb/spec.py | 2 +- .../src/relarena_core}/userdb/task.py | 2 +- .../relarena_core}/userdb/task.schema.json | 0 .../tests}/featurization/test_cache.py | 4 +- .../tests}/featurization/test_dfs.py | 8 +- .../tests}/featurization/test_warm_cache.py | 6 +- .../tests}/test_cache.py | 2 +- .../tests}/test_dataset.py | 2 +- .../relarena-core/tests/test_discovery.py | 84 ++++++++ .../tests}/test_identity.py | 2 +- .../tests}/test_metrics.py | 2 +- .../tests}/test_model.py | 4 +- .../tests/test_package_boundary.py | 29 +++ .../tests}/test_predict_contract.py | 2 +- .../tests}/test_search_space.py | 2 +- .../tests}/test_skeleton.py | 12 +- .../tests/test_standalone_runtime.py | 110 ++++++++++ .../core => relarena-core/tests}/test_tfm.py | 2 +- .../tests}/test_tuner.py | 4 +- .../tests}/userdb/test_ingest.py | 2 +- .../tests}/userdb/test_predict.py | 2 +- .../tests}/userdb/test_task.py | 2 +- packages/relarena/pyproject.toml | 18 +- packages/relarena/src/relarena/__init__.py | 32 +-- .../src/relarena/checksums/checksum.py | 2 +- packages/relarena/src/relarena/cli.py | 6 +- packages/relarena/src/relarena/dataset.py | 4 +- .../src/relarena/evaluation/leaderboard.py | 8 +- .../src/relarena/featurization/entity.py | 2 +- .../src/relarena/featurization/warm_cache.py | 6 +- packages/relarena/src/relarena/identity.py | 2 +- .../src/relarena/models/dummy/model.py | 10 +- .../src/relarena/models/graphsage/model.py | 8 +- .../src/relarena/models/kurversc/model.py | 6 +- .../src/relarena/models/lightgbm/model.py | 6 +- .../src/relarena/models/rdblearn/model.py | 14 +- .../src/relarena/models/rdblearn/tfm.py | 2 +- .../src/relarena/models/relgnn/model.py | 10 +- .../relarena/models/relgnn/preprocessing.py | 6 +- .../src/relarena/models/relgnn/warm_cache.py | 2 +- .../src/relarena/models/relgt/model.py | 8 +- .../src/relarena/models/relgt/tokenize.py | 4 +- .../src/relarena/models/relgt/warm_cache.py | 2 +- .../relarena/src/relarena/models/rt/export.py | 8 +- .../relarena/src/relarena/models/rt/model.py | 10 +- .../src/relarena/models/rt/warm_cache.py | 4 +- .../src/relarena/models/tabpfn_rel/context.py | 2 +- .../src/relarena/models/tabpfn_rel/model.py | 14 +- .../src/relarena/models/tabpfn_rel/tfm.py | 2 +- packages/relarena/src/relarena/refit.py | 10 +- packages/relarena/src/relarena/results.py | 2 +- packages/relarena/src/relarena/runner.py | 18 +- packages/relarena/src/relarena/tasks.py | 2 +- .../relarena/src/relarena/userdb/__init__.py | 6 +- .../relarena/userdb/relbench_v1/__init__.py | 2 +- .../relarena/tests/fixtures/cached_model.py | 4 +- .../relarena/tests/models/dummy/test_model.py | 2 +- .../tests/models/graphsage/test_model.py | 2 +- .../tests/models/kurversc/test_model.py | 4 +- .../tests/models/rdblearn/test_model.py | 6 +- .../tests/models/rdblearn/test_tfm.py | 2 +- .../tests/models/relgnn/test_model.py | 6 +- .../tests/models/relgnn/test_warm_cache.py | 4 +- .../relarena/tests/models/relgt/test_model.py | 4 +- .../tests/models/relgt/test_tokenize.py | 4 +- .../tests/models/relgt/test_warm_cache.py | 4 +- .../relarena/tests/models/rt/test_model.py | 6 +- .../tests/models/tabpfn_rel/test_context.py | 2 +- .../tests/models/tabpfn_rel/test_model.py | 6 +- .../relarena/tests/models/test_discovery.py | 10 +- .../relarena/tests/test_cache_extension.py | 2 +- packages/relarena/tests/test_cli.py | 2 +- packages/relarena/tests/test_dataset.py | 2 +- packages/relarena/tests/test_package_api.py | 61 ++++++ .../relarena/tests/test_plugin_discovery.py | 21 ++ packages/relarena/tests/test_refit.py | 8 +- packages/relarena/tests/test_results.py | 2 +- packages/relarena/tests/test_runner.py | 8 +- packages/relarena/tests/userdb/test_query.py | 10 +- pyproject.toml | 11 +- uv.lock | 56 ++++- workflows/audit_licenses.py | 2 + workflows/smoke_feature_cache.py | 10 +- workflows/verify_distributions.py | 30 ++- workflows/verify_relbench_v1.py | 4 +- workflows/warm_feature_cache.py | 4 +- 125 files changed, 980 insertions(+), 319 deletions(-) create mode 100644 packages/relarena-core/LICENSE create mode 100644 packages/relarena-core/NOTICE create mode 100644 packages/relarena-core/README.md create mode 100644 packages/relarena-core/pyproject.toml rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/__init__.py (55%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/cache.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/dataset.py (99%) create mode 100644 packages/relarena-core/src/relarena_core/discovery.py rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/featurization/__init__.py (64%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/featurization/cache.py (95%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/featurization/columns.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/featurization/dfs.py (98%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/featurization/warm_cache.py (90%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/fingerprints.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/identity.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/metrics.py (99%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/model.py (96%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/predict_contract.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/py.typed (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/registry.py (95%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/results.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/search_space.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/selection.py (88%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/system.py (90%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/tasks.py (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/tfm.py (98%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/tuner.py (93%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/__init__.py (52%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/_schema.py (94%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/database.schema.json (100%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/ingest.py (99%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/predict.py (97%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/query.py (94%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/spec.py (98%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/task.py (98%) rename packages/{relarena/src/relarena/core => relarena-core/src/relarena_core}/userdb/task.schema.json (100%) rename packages/{relarena/tests/core => relarena-core/tests}/featurization/test_cache.py (95%) rename packages/{relarena/tests/core => relarena-core/tests}/featurization/test_dfs.py (99%) rename packages/{relarena/tests/core => relarena-core/tests}/featurization/test_warm_cache.py (93%) rename packages/{relarena/tests/core => relarena-core/tests}/test_cache.py (99%) rename packages/{relarena/tests/core => relarena-core/tests}/test_dataset.py (94%) create mode 100644 packages/relarena-core/tests/test_discovery.py rename packages/{relarena/tests/core => relarena-core/tests}/test_identity.py (98%) rename packages/{relarena/tests/core => relarena-core/tests}/test_metrics.py (99%) rename packages/{relarena/tests/core => relarena-core/tests}/test_model.py (88%) create mode 100644 packages/relarena-core/tests/test_package_boundary.py rename packages/{relarena/tests/core => relarena-core/tests}/test_predict_contract.py (96%) rename packages/{relarena/tests/core => relarena-core/tests}/test_search_space.py (95%) rename packages/{relarena/tests/core => relarena-core/tests}/test_skeleton.py (90%) create mode 100644 packages/relarena-core/tests/test_standalone_runtime.py rename packages/{relarena/tests/core => relarena-core/tests}/test_tfm.py (98%) rename packages/{relarena/tests/core => relarena-core/tests}/test_tuner.py (96%) rename packages/{relarena/tests/core => relarena-core/tests}/userdb/test_ingest.py (98%) rename packages/{relarena/tests/core => relarena-core/tests}/userdb/test_predict.py (97%) rename packages/{relarena/tests/core => relarena-core/tests}/userdb/test_task.py (98%) create mode 100644 packages/relarena/tests/test_package_api.py create mode 100644 packages/relarena/tests/test_plugin_discovery.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96bd166..ce957e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,12 +16,17 @@ jobs: runs-on: ubuntu-latest env: UV_PYTHON: ${{ matrix.python-version }} + defaults: + run: + working-directory: relarena strategy: fail-fast: false matrix: python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: relarena - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} @@ -29,7 +34,7 @@ jobs: with: enable-cache: true - name: Install test dependencies - run: uv sync --all-packages --locked --group dev --group cpu --extra leaderboard --extra plots --extra rt + run: uv sync --locked --all-packages --group dev --group cpu --extra leaderboard --extra plots --extra rt - name: Verify test interpreter env: EXPECTED_PYTHON: ${{ matrix.python-version }} diff --git a/README.md b/README.md index f5d05ae..e6f1f26 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ import pandas as pd import relarena.models # registers the built-in models from relarena.evaluation import compute_leaderboard -from relarena.core.registry import registry +from relarena_core.registry import registry from relarena.results import summary_to_dataframe from relarena.runner import run_experiment from relarena.tasks import list_entity_tasks @@ -246,7 +246,7 @@ preprocessing and GPU-bound training have different hardware requirements. RelAr permits methods to compute preprocessing artifacts once and cache them on disk before a run. Caching is not required. RelArena provides an **optional, experimental** helper API in -[`relarena.core.cache`](packages/relarena/src/relarena/cache.py) for local paths, miss policies, private scratch +[`relarena_core.cache`](packages/relarena/src/relarena/cache.py) for local paths, miss policies, private scratch computation, and atomic publication. A method may ignore this API and implement caching independently. The helper does not bring cache warming into a timed RelArena experiment; preprocessing scripts still run separately, so their runtime is not currently included in the diff --git a/docs/adding-a-model.md b/docs/adding-a-model.md index 82a04ea..458a28d 100644 --- a/docs/adding-a-model.md +++ b/docs/adding-a-model.md @@ -33,9 +33,9 @@ One folder may register **several** models: `dummy` → `constant-global` + import numpy as np from relbench.base import Database, EntityTask, Table -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace MYMODEL_SPACE = SearchSpace(space=_config_space(), default_overrides={}) @@ -142,8 +142,8 @@ import numpy as np from relbench.base import EntityTask from relarena.dataset import InnerSplit, OuterSplit -from relarena.core.registry import register_system -from relarena.core.system import RelArenaSystem +from relarena_core.registry import register_system +from relarena_core.system import RelArenaSystem @register_system @@ -378,7 +378,7 @@ training settings that do not affect it. Content fingerprints and explicit preprocessing versions can be used to invalidate artifacts when their inputs or meaning change. -See [`relarena.core.cache`](../src/relarena/cache.py) for the API and its design +See [`relarena_core.cache`](../src/relarena/cache.py) for the API and its design notes, and [`tests/fixtures/cached_model.py`](../tests/fixtures/cached_model.py) for a compact end-to-end example. diff --git a/docs/predictive-task.md b/docs/predictive-task.md index b2239e6..6715d83 100644 --- a/docs/predictive-task.md +++ b/docs/predictive-task.md @@ -79,8 +79,8 @@ default configuration when `n_trials=0`; a positive budget enables temporal tuni different `val_timestamp` and `test_timestamp` values over the same tables. The two files' shapes are defined by -[`database.schema.json`](../packages/relarena-core/src/relarena.core/userdb/database.schema.json) and -[`task.schema.json`](../packages/relarena-core/src/relarena.core/userdb/task.schema.json) — JSON Schemas with a +[`database.schema.json`](../packages/relarena-core/src/relarena_core/userdb/database.schema.json) and +[`task.schema.json`](../packages/relarena-core/src/relarena_core/userdb/task.schema.json) — JSON Schemas with a description on every field, validated on load, so a malformed file fails fast with a pointer to the offending field rather than an opaque error later. @@ -266,7 +266,7 @@ maximum depth up to which fastdfs joins. the final fit, and prediction. The first RPI run fills the local store and later runs over the same inputs read it back. Nothing is uploaded. Omit `cache_dir` to fall back to `RELARENA_CACHE_DIR`, or to compute without persistent caching when -neither is set. The underlying `relarena.core.cache` API is optional and experimental; +neither is set. The underlying `relarena_core.cache` API is optional and experimental; models may implement caching independently. ```python diff --git a/docs/temporal-validation.md b/docs/temporal-validation.md index db172ec..a26f904 100644 --- a/docs/temporal-validation.md +++ b/docs/temporal-validation.md @@ -82,10 +82,10 @@ post-boundary data, and it does not recreate the alternative evaluation regime that advances the database to each test entity's timestamp. Shared split construction lives in core's -[`dataset.py`](../packages/relarena-core/src/relarena.core/dataset.py), with named +[`dataset.py`](../packages/relarena-core/src/relarena_core/dataset.py), with named benchmark loading in RelArena's [`dataset.py`](../packages/relarena/src/relarena/dataset.py). Tuning lives in core's -[`tuner.py`](../packages/relarena-core/src/relarena.core/tuner.py); benchmark +[`tuner.py`](../packages/relarena-core/src/relarena_core/tuner.py); benchmark orchestration and final test scoring live in [`runner.py`](../packages/relarena/src/relarena/runner.py) and [`refit.py`](../packages/relarena/src/relarena/refit.py). diff --git a/examples/tabpfn_rel_caching.py b/examples/tabpfn_rel_caching.py index 36eb520..2cca6eb 100644 --- a/examples/tabpfn_rel_caching.py +++ b/examples/tabpfn_rel_caching.py @@ -32,14 +32,14 @@ import numpy as np import pandas as pd -from relarena.core.cache import CacheConfig -from relarena.core.dataset import OuterSplit, concat_tables -from relarena.core.featurization import build_dfs_features -from relarena.core.featurization import dfs as dfs_mod -from relarena.core.featurization.warm_cache import warm_dfs_cache from relarena.dataset import RelBenchDatasetTask from relarena.models.tabpfn_rel.model import TABPFN_REL_LOCAL_SPACE, TabPFNRelModel from relarena.models.tabpfn_rel.tfm import default_device +from relarena_core.cache import CacheConfig +from relarena_core.dataset import OuterSplit, concat_tables +from relarena_core.featurization import build_dfs_features +from relarena_core.featurization import dfs as dfs_mod +from relarena_core.featurization.warm_cache import warm_dfs_cache #: A reasonably sized RelBench entity task: small enough to run, big enough that the #: DFS cost is visible. Swap for e.g. ("rel-hm", "user-churn") for a heavier one. diff --git a/packages/relarena-core/LICENSE b/packages/relarena-core/LICENSE new file mode 100644 index 0000000..830a28f --- /dev/null +++ b/packages/relarena-core/LICENSE @@ -0,0 +1,200 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 PriorLabs GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and diff --git a/packages/relarena-core/NOTICE b/packages/relarena-core/NOTICE new file mode 100644 index 0000000..e57522f --- /dev/null +++ b/packages/relarena-core/NOTICE @@ -0,0 +1,16 @@ +RelArena Core +Copyright 2026 PriorLabs GmbH + +Licensed under the Apache License, Version 2.0. See the LICENSE file. + +The shared Deep Feature Synthesis and tabular-foundation-model fitting recipe +is a method adaptation of RDBLearn, licensed under Apache-2.0: +Copyright (c) HKU Shanghai X-Lab +https://github.com/HKUSHXLab/rdblearn + +The non-stratified train downsampling in src/relarena_core/tfm.py is the closest +adaptation. The implementation is original code; no RDBLearn source files are +copied into this distribution. + +Datasets and model weights are not distributed with this package. Dependencies, +downloaded datasets and inference backends retain their own upstream terms. diff --git a/packages/relarena-core/README.md b/packages/relarena-core/README.md new file mode 100644 index 0000000..267b374 --- /dev/null +++ b/packages/relarena-core/README.md @@ -0,0 +1,11 @@ +# RelArena Core + +Shared model contracts, temporal validation, caching and predictive queries for +relational models. This distribution is developed in the RelArena monorepo. + +```bash +pip install relarena-core +``` + +DFS preprocessing is available through `relarena-core[dfs]`. Model packages +provide their own inference backends. diff --git a/packages/relarena-core/pyproject.toml b/packages/relarena-core/pyproject.toml new file mode 100644 index 0000000..bbf9fcb --- /dev/null +++ b/packages/relarena-core/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["uv_build>=0.11.15"] +build-backend = "uv_build" + +[project] +name = "relarena-core" +version = "0.0.1" +description = "Shared contracts, temporal validation and predictive queries for relational models." +readme = "README.md" +license = "Apache-2.0" +license-files = ["LICENSE", "NOTICE"] +authors = [{ name = "Prior Labs" }] +requires-python = ">=3.11,<3.13" +dependencies = [ + "relbench==2.1.2", + "numpy>=1.24", + "pandas>=2.3.3,<3.0", + "scikit-learn>=1.3", + "configspace>=1.0", + "pyyaml>=6.0", + "jsonschema>=4.0", + "duckdb>=1.2.2", + "torch", +] + +[project.optional-dependencies] +dfs = ["fastdfs>=1.1", "setuptools<82"] diff --git a/packages/relarena/src/relarena/core/__init__.py b/packages/relarena-core/src/relarena_core/__init__.py similarity index 55% rename from packages/relarena/src/relarena/core/__init__.py rename to packages/relarena-core/src/relarena_core/__init__.py index 0ee3e1f..5015106 100644 --- a/packages/relarena/src/relarena/core/__init__.py +++ b/packages/relarena-core/src/relarena_core/__init__.py @@ -1,19 +1,20 @@ """Shared contracts and runtime for relational prediction.""" -from relarena.core.cache import CacheConfig, CacheMiss, cache_key, cached_artifact -from relarena.core.dataset import InnerSplit, OuterSplit, Split, TaskSource -from relarena.core.identity import RunIdentity -from relarena.core.model import RelArenaModel -from relarena.core.registry import ( +from relarena_core.cache import CacheConfig, CacheMiss, cache_key, cached_artifact +from relarena_core.dataset import InnerSplit, OuterSplit, Split, TaskSource +from relarena_core.discovery import discover_models +from relarena_core.identity import RunIdentity +from relarena_core.model import RelArenaModel +from relarena_core.registry import ( MethodRegistry, ModelRegistry, register_model, register_system, registry, ) -from relarena.core.results import SystemResult, TrialResult -from relarena.core.system import RelArenaSystem -from relarena.core.tuner import tune +from relarena_core.results import SystemResult, TrialResult +from relarena_core.system import RelArenaSystem +from relarena_core.tuner import tune __version__ = "0.0.1" __all__ = [ @@ -25,6 +26,7 @@ "Split", "InnerSplit", "OuterSplit", + "discover_models", "RunIdentity", "RelArenaModel", "RelArenaSystem", diff --git a/packages/relarena/src/relarena/core/cache.py b/packages/relarena-core/src/relarena_core/cache.py similarity index 100% rename from packages/relarena/src/relarena/core/cache.py rename to packages/relarena-core/src/relarena_core/cache.py diff --git a/packages/relarena/src/relarena/core/dataset.py b/packages/relarena-core/src/relarena_core/dataset.py similarity index 99% rename from packages/relarena/src/relarena/core/dataset.py rename to packages/relarena-core/src/relarena_core/dataset.py index e15a96a..50d7a64 100644 --- a/packages/relarena/src/relarena/core/dataset.py +++ b/packages/relarena-core/src/relarena_core/dataset.py @@ -8,8 +8,8 @@ import pandas as pd from relbench.base import Database, Dataset, EntityTask, Table -from relarena.core.identity import RunIdentity -from relarena.core.metrics import primary_metric +from relarena_core.identity import RunIdentity +from relarena_core.metrics import primary_metric def clean_database( diff --git a/packages/relarena-core/src/relarena_core/discovery.py b/packages/relarena-core/src/relarena_core/discovery.py new file mode 100644 index 0000000..bdb5758 --- /dev/null +++ b/packages/relarena-core/src/relarena_core/discovery.py @@ -0,0 +1,36 @@ +"""Load installed model modules so their decorators populate the shared registry.""" + +from __future__ import annotations + +from importlib.metadata import entry_points +from threading import RLock + +_loaded: set[tuple[str, str]] = set() +_lock = RLock() + + +def discover_models() -> None: + """Import model modules declared in the relarena.models entry-point group. + + Module imports execute registration decorators against the shared registry. + Successful entries load once per process; failed imports remain retryable + and raise an error naming the plugin. Importing core does not run discovery. + """ + with _lock: + for entry in sorted( + entry_points(group="relarena.models"), key=lambda e: (e.name, e.value) + ): + key = (entry.name, entry.value) + if key in _loaded: + continue + try: + entry.load() + except Exception as exc: + raise RuntimeError( + f"Could not register model plugin {entry.name!r} " + f"({entry.value}). Check its installation and dependencies." + ) from exc + _loaded.add(key) + + +__all__ = ["discover_models"] diff --git a/packages/relarena/src/relarena/core/featurization/__init__.py b/packages/relarena-core/src/relarena_core/featurization/__init__.py similarity index 64% rename from packages/relarena/src/relarena/core/featurization/__init__.py rename to packages/relarena-core/src/relarena_core/featurization/__init__.py index a7ea18c..cf8197f 100644 --- a/packages/relarena/src/relarena/core/featurization/__init__.py +++ b/packages/relarena-core/src/relarena_core/featurization/__init__.py @@ -1,5 +1,5 @@ """Shared relational deep feature synthesis.""" -from relarena.core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features +from relarena_core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features __all__ = ["DFS_MAX_DEPTH", "build_dfs_features"] diff --git a/packages/relarena/src/relarena/core/featurization/cache.py b/packages/relarena-core/src/relarena_core/featurization/cache.py similarity index 95% rename from packages/relarena/src/relarena/core/featurization/cache.py rename to packages/relarena-core/src/relarena_core/featurization/cache.py index 2c20fdf..bcd8cd4 100644 --- a/packages/relarena/src/relarena/core/featurization/cache.py +++ b/packages/relarena-core/src/relarena_core/featurization/cache.py @@ -7,7 +7,7 @@ import pandas as pd -from relarena.core.cache import CacheConfig, cached_artifact +from relarena_core.cache import CacheConfig, cached_artifact def cached_frame( diff --git a/packages/relarena/src/relarena/core/featurization/columns.py b/packages/relarena-core/src/relarena_core/featurization/columns.py similarity index 100% rename from packages/relarena/src/relarena/core/featurization/columns.py rename to packages/relarena-core/src/relarena_core/featurization/columns.py diff --git a/packages/relarena/src/relarena/core/featurization/dfs.py b/packages/relarena-core/src/relarena_core/featurization/dfs.py similarity index 98% rename from packages/relarena/src/relarena/core/featurization/dfs.py rename to packages/relarena-core/src/relarena_core/featurization/dfs.py index b45d359..d38dffd 100644 --- a/packages/relarena/src/relarena/core/featurization/dfs.py +++ b/packages/relarena-core/src/relarena_core/featurization/dfs.py @@ -49,11 +49,11 @@ import pandas as pd from relbench.base import Database, EntityTask, Table -from relarena.core.cache import CacheConfig, cache_key -from relarena.core.featurization.cache import cached_frame -from relarena.core.featurization.columns import type_columns -from relarena.core.fingerprints import database_checksum, table_checksum -from relarena.core.identity import RunIdentity +from relarena_core.cache import CacheConfig, cache_key +from relarena_core.featurization.cache import cached_frame +from relarena_core.featurization.columns import type_columns +from relarena_core.fingerprints import database_checksum, table_checksum +from relarena_core.identity import RunIdentity if TYPE_CHECKING: # pragma: no cover - typing only from fastdfs import RDB @@ -535,7 +535,7 @@ def build_dfs_features( across splits. """ # The DFS deps are an extra, so these imports stay function-local: at module - # scope they make `relarena.core.featurization` unimportable without the extra, + # scope they make `relarena_core.featurization` unimportable without the extra, # which silently drops every model that imports it from the registry. from fastdfs import DFSConfig, compute_dfs_features from fastdfs.dfs import dfs_feature_column_name, get_dfs_engine diff --git a/packages/relarena/src/relarena/core/featurization/warm_cache.py b/packages/relarena-core/src/relarena_core/featurization/warm_cache.py similarity index 90% rename from packages/relarena/src/relarena/core/featurization/warm_cache.py rename to packages/relarena-core/src/relarena_core/featurization/warm_cache.py index a07d18f..5c12c60 100644 --- a/packages/relarena/src/relarena/core/featurization/warm_cache.py +++ b/packages/relarena-core/src/relarena_core/featurization/warm_cache.py @@ -2,9 +2,9 @@ from __future__ import annotations -from relarena.core.cache import CacheConfig -from relarena.core.dataset import TaskSource, concat_tables -from relarena.core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features +from relarena_core.cache import CacheConfig +from relarena_core.dataset import TaskSource, concat_tables +from relarena_core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features def warm_dfs_cache( diff --git a/packages/relarena/src/relarena/core/fingerprints.py b/packages/relarena-core/src/relarena_core/fingerprints.py similarity index 100% rename from packages/relarena/src/relarena/core/fingerprints.py rename to packages/relarena-core/src/relarena_core/fingerprints.py diff --git a/packages/relarena/src/relarena/core/identity.py b/packages/relarena-core/src/relarena_core/identity.py similarity index 100% rename from packages/relarena/src/relarena/core/identity.py rename to packages/relarena-core/src/relarena_core/identity.py diff --git a/packages/relarena/src/relarena/core/metrics.py b/packages/relarena-core/src/relarena_core/metrics.py similarity index 99% rename from packages/relarena/src/relarena/core/metrics.py rename to packages/relarena-core/src/relarena_core/metrics.py index 5cc7889..1ea07e2 100644 --- a/packages/relarena/src/relarena/core/metrics.py +++ b/packages/relarena-core/src/relarena_core/metrics.py @@ -107,7 +107,7 @@ def get_metric(metric: MetricLike) -> Metric: name = _metric_name(metric) if name not in _METRICS: raise KeyError( - f"Unknown metric '{name}'; add it to _METRICS in relarena.core/metrics.py" + f"Unknown metric '{name}'; add it to _METRICS in relarena_core/metrics.py" ) return _METRICS[name] diff --git a/packages/relarena/src/relarena/core/model.py b/packages/relarena-core/src/relarena_core/model.py similarity index 96% rename from packages/relarena/src/relarena/core/model.py rename to packages/relarena-core/src/relarena_core/model.py index 9a920ec..efb97ac 100644 --- a/packages/relarena/src/relarena/core/model.py +++ b/packages/relarena-core/src/relarena_core/model.py @@ -21,9 +21,9 @@ import numpy as np from relbench.base import Database, EntityTask, Table, TaskType -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity -from relarena.core.tasks import ENTITY_TASK_TYPES +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity +from relarena_core.tasks import ENTITY_TASK_TYPES class RelArenaModel(ABC): diff --git a/packages/relarena/src/relarena/core/predict_contract.py b/packages/relarena-core/src/relarena_core/predict_contract.py similarity index 100% rename from packages/relarena/src/relarena/core/predict_contract.py rename to packages/relarena-core/src/relarena_core/predict_contract.py diff --git a/packages/relarena/src/relarena/core/py.typed b/packages/relarena-core/src/relarena_core/py.typed similarity index 100% rename from packages/relarena/src/relarena/core/py.typed rename to packages/relarena-core/src/relarena_core/py.typed diff --git a/packages/relarena/src/relarena/core/registry.py b/packages/relarena-core/src/relarena_core/registry.py similarity index 95% rename from packages/relarena/src/relarena/core/registry.py rename to packages/relarena-core/src/relarena_core/registry.py index cb5c11c..e2fe036 100644 --- a/packages/relarena/src/relarena/core/registry.py +++ b/packages/relarena-core/src/relarena_core/registry.py @@ -10,9 +10,9 @@ from dataclasses import dataclass from typing import Callable, Iterator, Type, TypeAlias -from relarena.core.model import RelArenaModel -from relarena.core.search_space import SearchSpaceProvider -from relarena.core.system import RelArenaSystem +from relarena_core.model import RelArenaModel +from relarena_core.search_space import SearchSpaceProvider +from relarena_core.system import RelArenaSystem Method: TypeAlias = type[RelArenaModel] | type[RelArenaSystem] @@ -100,7 +100,9 @@ def names(self) -> list[str]: def _entry(self, name: str) -> RegistryEntry: if name not in self._entries: - hint = " Import relarena.models to register built-in models." + hint = ( + " Call relarena_core.discover_models() to load installed model plugins." + ) raise KeyError( f"No method registered under '{name}'. Known: {self.names()}.{hint}" ) diff --git a/packages/relarena/src/relarena/core/results.py b/packages/relarena-core/src/relarena_core/results.py similarity index 100% rename from packages/relarena/src/relarena/core/results.py rename to packages/relarena-core/src/relarena_core/results.py diff --git a/packages/relarena/src/relarena/core/search_space.py b/packages/relarena-core/src/relarena_core/search_space.py similarity index 100% rename from packages/relarena/src/relarena/core/search_space.py rename to packages/relarena-core/src/relarena_core/search_space.py diff --git a/packages/relarena/src/relarena/core/selection.py b/packages/relarena-core/src/relarena_core/selection.py similarity index 88% rename from packages/relarena/src/relarena/core/selection.py rename to packages/relarena-core/src/relarena_core/selection.py index bb00a91..be86d72 100644 --- a/packages/relarena/src/relarena/core/selection.py +++ b/packages/relarena-core/src/relarena_core/selection.py @@ -3,8 +3,8 @@ import math from typing import Callable -from relarena.core.metrics import is_better -from relarena.core.results import TrialResult +from relarena_core.metrics import is_better +from relarena_core.results import TrialResult def select_best(trials: list[TrialResult], metric: Callable[..., float]) -> TrialResult: diff --git a/packages/relarena/src/relarena/core/system.py b/packages/relarena-core/src/relarena_core/system.py similarity index 90% rename from packages/relarena/src/relarena/core/system.py rename to packages/relarena-core/src/relarena_core/system.py index 62cbe45..44cc306 100644 --- a/packages/relarena/src/relarena/core/system.py +++ b/packages/relarena-core/src/relarena_core/system.py @@ -15,10 +15,10 @@ import numpy as np from relbench.base import EntityTask, TaskType -from relarena.core.cache import CacheConfig -from relarena.core.dataset import InnerSplit, OuterSplit -from relarena.core.identity import RunIdentity -from relarena.core.tasks import ENTITY_TASK_TYPES +from relarena_core.cache import CacheConfig +from relarena_core.dataset import InnerSplit, OuterSplit +from relarena_core.identity import RunIdentity +from relarena_core.tasks import ENTITY_TASK_TYPES class RelArenaSystem(ABC): diff --git a/packages/relarena/src/relarena/core/tasks.py b/packages/relarena-core/src/relarena_core/tasks.py similarity index 100% rename from packages/relarena/src/relarena/core/tasks.py rename to packages/relarena-core/src/relarena_core/tasks.py diff --git a/packages/relarena/src/relarena/core/tfm.py b/packages/relarena-core/src/relarena_core/tfm.py similarity index 98% rename from packages/relarena/src/relarena/core/tfm.py rename to packages/relarena-core/src/relarena_core/tfm.py index 823149b..593d99d 100644 --- a/packages/relarena/src/relarena/core/tfm.py +++ b/packages/relarena-core/src/relarena_core/tfm.py @@ -10,7 +10,7 @@ import pandas as pd from relbench.base import TaskType -from relarena.core.predict_contract import predict_to_contract +from relarena_core.predict_contract import predict_to_contract class SklearnClassifier(Protocol): @@ -179,7 +179,7 @@ def predict_tfm(fitted: FittedTFM, df: pd.DataFrame) -> np.ndarray: Reindexes to the training column order (so the TFM sees the same schema), then delegates the sklearn-output -> evaluate-contract reshaping to - `relarena.core.predict_contract.predict_to_contract`. + `relarena_core.predict_contract.predict_to_contract`. Regression requests `output_type="median"` when the estimator supports it — an explicit output_type parameter, or a **kwargs passthrough: the primary diff --git a/packages/relarena/src/relarena/core/tuner.py b/packages/relarena-core/src/relarena_core/tuner.py similarity index 93% rename from packages/relarena/src/relarena/core/tuner.py rename to packages/relarena-core/src/relarena_core/tuner.py index 4e2ee3e..f7c0b60 100644 --- a/packages/relarena/src/relarena/core/tuner.py +++ b/packages/relarena-core/src/relarena_core/tuner.py @@ -8,7 +8,7 @@ * every trial records its configuration, metrics, phase-split wall-clock times, and optionally its validation/test predictions as useful analysis metadata. -Callers select the best configuration with `relarena.core.selection.select_best`. +Callers select the best configuration with `relarena_core.selection.select_best`. This module runs the trials and returns their records. """ @@ -22,13 +22,13 @@ from relbench.base import EntityTask -from relarena.core.cache import CacheConfig -from relarena.core.dataset import InnerSplit -from relarena.core.identity import RunIdentity -from relarena.core.metrics import evaluate_predictions, primary_metric -from relarena.core.model import RelArenaModel -from relarena.core.results import TrialResult, config_id_for -from relarena.core.search_space import ( +from relarena_core.cache import CacheConfig +from relarena_core.dataset import InnerSplit +from relarena_core.identity import RunIdentity +from relarena_core.metrics import evaluate_predictions, primary_metric +from relarena_core.model import RelArenaModel +from relarena_core.results import TrialResult, config_id_for +from relarena_core.search_space import ( SearchSpace, SearchSpaceProvider, TaskStats, diff --git a/packages/relarena/src/relarena/core/userdb/__init__.py b/packages/relarena-core/src/relarena_core/userdb/__init__.py similarity index 52% rename from packages/relarena/src/relarena/core/userdb/__init__.py rename to packages/relarena-core/src/relarena_core/userdb/__init__.py index 3410cb8..9cd5b0e 100644 --- a/packages/relarena/src/relarena/core/userdb/__init__.py +++ b/packages/relarena-core/src/relarena_core/userdb/__init__.py @@ -1,8 +1,8 @@ """Relational predictive queries over user-supplied databases.""" -from relarena.core.userdb.ingest import DatabaseSpec -from relarena.core.userdb.query import PredictiveQuery, PredictiveQuerySpec -from relarena.core.userdb.spec import PredictiveTaskSpec +from relarena_core.userdb.ingest import DatabaseSpec +from relarena_core.userdb.query import PredictiveQuery, PredictiveQuerySpec +from relarena_core.userdb.spec import PredictiveTaskSpec __all__ = [ "DatabaseSpec", diff --git a/packages/relarena/src/relarena/core/userdb/_schema.py b/packages/relarena-core/src/relarena_core/userdb/_schema.py similarity index 94% rename from packages/relarena/src/relarena/core/userdb/_schema.py rename to packages/relarena-core/src/relarena_core/userdb/_schema.py index 0fdb89e..2b685f8 100644 --- a/packages/relarena/src/relarena/core/userdb/_schema.py +++ b/packages/relarena-core/src/relarena_core/userdb/_schema.py @@ -10,7 +10,7 @@ def load_schema(name: str) -> dict: """Load a JSON Schema shipped alongside this package, by file name.""" - return json.loads((files("relarena.core.userdb") / name).read_text()) + return json.loads((files("relarena_core.userdb") / name).read_text()) def validate(raw: object, schema: dict, *, kind: str) -> None: diff --git a/packages/relarena/src/relarena/core/userdb/database.schema.json b/packages/relarena-core/src/relarena_core/userdb/database.schema.json similarity index 100% rename from packages/relarena/src/relarena/core/userdb/database.schema.json rename to packages/relarena-core/src/relarena_core/userdb/database.schema.json diff --git a/packages/relarena/src/relarena/core/userdb/ingest.py b/packages/relarena-core/src/relarena_core/userdb/ingest.py similarity index 99% rename from packages/relarena/src/relarena/core/userdb/ingest.py rename to packages/relarena-core/src/relarena_core/userdb/ingest.py index 4e081ba..4974f71 100644 --- a/packages/relarena/src/relarena/core/userdb/ingest.py +++ b/packages/relarena-core/src/relarena_core/userdb/ingest.py @@ -16,7 +16,7 @@ import yaml from relbench.base import Database, Dataset, Table -from relarena.core.userdb._schema import load_schema, validate +from relarena_core.userdb._schema import load_schema, validate #: JSON Schema for a database YAML; the single source of truth for its shape. _DB_SCHEMA = load_schema("database.schema.json") diff --git a/packages/relarena/src/relarena/core/userdb/predict.py b/packages/relarena-core/src/relarena_core/userdb/predict.py similarity index 97% rename from packages/relarena/src/relarena/core/userdb/predict.py rename to packages/relarena-core/src/relarena_core/userdb/predict.py index 1ed5f83..11aa174 100644 --- a/packages/relarena/src/relarena/core/userdb/predict.py +++ b/packages/relarena-core/src/relarena_core/userdb/predict.py @@ -18,10 +18,10 @@ import pandas as pd from relbench.base import Database, EntityTask, Table -from relarena.core.dataset import _copy_timeless_tables +from relarena_core.dataset import _copy_timeless_tables if TYPE_CHECKING: - from relarena.core.model import RelArenaModel + from relarena_core.model import RelArenaModel #: Entity universe selector: `"all"` (every entity existing at the anchor) or an #: explicit collection of entity ids. diff --git a/packages/relarena/src/relarena/core/userdb/query.py b/packages/relarena-core/src/relarena_core/userdb/query.py similarity index 94% rename from packages/relarena/src/relarena/core/userdb/query.py rename to packages/relarena-core/src/relarena_core/userdb/query.py index ebfd8e1..804d5f6 100644 --- a/packages/relarena/src/relarena/core/userdb/query.py +++ b/packages/relarena-core/src/relarena_core/userdb/query.py @@ -16,31 +16,31 @@ import warnings from dataclasses import dataclass -from importlib import import_module from pathlib import Path import pandas as pd import yaml from relbench.base import EntityTask -from relarena.core.cache import CacheConfig, resolve_cache_config -from relarena.core.dataset import TaskSource, concat_tables -from relarena.core.identity import ( +from relarena_core.cache import CacheConfig, resolve_cache_config +from relarena_core.dataset import TaskSource, concat_tables +from relarena_core.discovery import discover_models +from relarena_core.identity import ( RunIdentity, database_schema_fingerprint, task_spec_fingerprint, ) -from relarena.core.model import RelArenaModel -from relarena.core.registry import registry -from relarena.core.search_space import TaskStats, resolve_search_space -from relarena.core.selection import select_best -from relarena.core.system import RelArenaSystem -from relarena.core.tuner import tune as run_tuning -from relarena.core.userdb._schema import load_schema, validate -from relarena.core.userdb.ingest import DatabaseSpec, build_dataset -from relarena.core.userdb.predict import EntitySelector, predict_at -from relarena.core.userdb.spec import PredictiveTaskSpec -from relarena.core.userdb.task import UserEntityTask +from relarena_core.model import RelArenaModel +from relarena_core.registry import registry +from relarena_core.search_space import TaskStats, resolve_search_space +from relarena_core.selection import select_best +from relarena_core.system import RelArenaSystem +from relarena_core.tuner import tune as run_tuning +from relarena_core.userdb._schema import load_schema, validate +from relarena_core.userdb.ingest import DatabaseSpec, build_dataset +from relarena_core.userdb.predict import EntitySelector, predict_at +from relarena_core.userdb.spec import PredictiveTaskSpec +from relarena_core.userdb.task import UserEntityTask #: JSON Schema for a task YAML; the single source of truth for its accepted shape. _TASK_SCHEMA = load_schema("task.schema.json") @@ -113,9 +113,9 @@ def fit( `cache_dir` is a local directory that caches DFS features across tuning, the final fit, and later `predict`, useful for repeated runs on a large custom database. Omit it to fall back to `RELARENA_CACHE_DIR`, or to use - no persistent cache when that variable is unset. See `relarena.core.cache`. + no persistent cache when that variable is unset. See `relarena_core.cache`. """ - import_module("relarena.models") + discover_models() cache = resolve_cache_config(cache_dir, on_miss="fill") self._warn_schema_only_cache(cache) @@ -184,7 +184,7 @@ def precompute_cache(self, cache_dir: str | Path) -> str | Path: its own anchor features on top. The resulting artifacts are shared by all DFS models. """ - from relarena.core.featurization.warm_cache import warm_dfs_cache + from relarena_core.featurization.warm_cache import warm_dfs_cache cache = resolve_cache_config(cache_dir, on_miss="fill") self._warn_schema_only_cache(cache) diff --git a/packages/relarena/src/relarena/core/userdb/spec.py b/packages/relarena-core/src/relarena_core/userdb/spec.py similarity index 98% rename from packages/relarena/src/relarena/core/userdb/spec.py rename to packages/relarena-core/src/relarena_core/userdb/spec.py index 8ec62e6..951e566 100644 --- a/packages/relarena/src/relarena/core/userdb/spec.py +++ b/packages/relarena-core/src/relarena_core/userdb/spec.py @@ -7,7 +7,7 @@ import pandas as pd from relbench.base import TaskType -from relarena.core.userdb.predict import EntitySelector +from relarena_core.userdb.predict import EntitySelector #: String aliases accepted for `task_type`, restricted to the entity task types #: RelArena supports. diff --git a/packages/relarena/src/relarena/core/userdb/task.py b/packages/relarena-core/src/relarena_core/userdb/task.py similarity index 98% rename from packages/relarena/src/relarena/core/userdb/task.py rename to packages/relarena-core/src/relarena_core/userdb/task.py index 628c997..72f55e1 100644 --- a/packages/relarena/src/relarena/core/userdb/task.py +++ b/packages/relarena-core/src/relarena_core/userdb/task.py @@ -7,7 +7,7 @@ from relbench.base import Database, Dataset, EntityTask, Table, TaskType from relbench.metrics import accuracy, average_precision, f1, mae, r2, rmse, roc_auc -from relarena.core.userdb.spec import PredictiveTaskSpec +from relarena_core.userdb.spec import PredictiveTaskSpec #: RelBench's standard metric lists per entity task type (primary metric last). _METRICS_BY_TASK_TYPE = { diff --git a/packages/relarena/src/relarena/core/userdb/task.schema.json b/packages/relarena-core/src/relarena_core/userdb/task.schema.json similarity index 100% rename from packages/relarena/src/relarena/core/userdb/task.schema.json rename to packages/relarena-core/src/relarena_core/userdb/task.schema.json diff --git a/packages/relarena/tests/core/featurization/test_cache.py b/packages/relarena-core/tests/featurization/test_cache.py similarity index 95% rename from packages/relarena/tests/core/featurization/test_cache.py rename to packages/relarena-core/tests/featurization/test_cache.py index 7f20bd6..dc9ee5b 100644 --- a/packages/relarena/tests/core/featurization/test_cache.py +++ b/packages/relarena-core/tests/featurization/test_cache.py @@ -7,8 +7,8 @@ import pandas as pd import pytest -from relarena.core.cache import CacheConfig, CacheMiss -from relarena.core.featurization.cache import cached_frame +from relarena_core.cache import CacheConfig, CacheMiss +from relarena_core.featurization.cache import cached_frame def _frame(value: int = 1) -> pd.DataFrame: diff --git a/packages/relarena/tests/core/featurization/test_dfs.py b/packages/relarena-core/tests/featurization/test_dfs.py similarity index 99% rename from packages/relarena/tests/core/featurization/test_dfs.py rename to packages/relarena-core/tests/featurization/test_dfs.py index 0987028..4637dfa 100644 --- a/packages/relarena/tests/core/featurization/test_dfs.py +++ b/packages/relarena-core/tests/featurization/test_dfs.py @@ -12,14 +12,14 @@ from fastdfs.dfs import dfs_feature_column_name from relbench.base import Database, Table -import relarena.core.featurization.dfs as dfs_mod -from relarena.core.cache import CacheConfig -from relarena.core.featurization.dfs import ( +import relarena_core.featurization.dfs as dfs_mod +from relarena_core.cache import CacheConfig +from relarena_core.featurization.dfs import ( TARGET_HISTORY_TABLE_NAME, _DepthCache, _temporal_diff, ) -from relarena.core.identity import RunIdentity +from relarena_core.identity import RunIdentity def _toy_db() -> Database: diff --git a/packages/relarena/tests/core/featurization/test_warm_cache.py b/packages/relarena-core/tests/featurization/test_warm_cache.py similarity index 93% rename from packages/relarena/tests/core/featurization/test_warm_cache.py rename to packages/relarena-core/tests/featurization/test_warm_cache.py index 8f21b10..f60ca1f 100644 --- a/packages/relarena/tests/core/featurization/test_warm_cache.py +++ b/packages/relarena-core/tests/featurization/test_warm_cache.py @@ -7,9 +7,9 @@ import pytest -from relarena.core.cache import CacheConfig -from relarena.core.featurization import warm_cache -from relarena.core.identity import RunIdentity +from relarena_core.cache import CacheConfig +from relarena_core.featurization import warm_cache +from relarena_core.identity import RunIdentity def test__warm_dfs_cache__uses_one_shared_preprocessor_for_both_phases( diff --git a/packages/relarena/tests/core/test_cache.py b/packages/relarena-core/tests/test_cache.py similarity index 99% rename from packages/relarena/tests/core/test_cache.py rename to packages/relarena-core/tests/test_cache.py index f0908a4..1d06812 100644 --- a/packages/relarena/tests/core/test_cache.py +++ b/packages/relarena-core/tests/test_cache.py @@ -11,7 +11,7 @@ import pytest -from relarena.core.cache import ( +from relarena_core.cache import ( CACHE_DIR_ENV, DISABLE_CACHE_ENV, LEGACY_DISABLE_CACHE_ENV, diff --git a/packages/relarena/tests/core/test_dataset.py b/packages/relarena-core/tests/test_dataset.py similarity index 94% rename from packages/relarena/tests/core/test_dataset.py rename to packages/relarena-core/tests/test_dataset.py index 6cacc8d..1ce97bb 100644 --- a/packages/relarena/tests/core/test_dataset.py +++ b/packages/relarena-core/tests/test_dataset.py @@ -3,7 +3,7 @@ import pandas as pd from relbench.base import Table -from relarena.core.dataset import concat_tables +from relarena_core.dataset import concat_tables def test_concat_tables_unions_rows_and_keeps_schema() -> None: diff --git a/packages/relarena-core/tests/test_discovery.py b/packages/relarena-core/tests/test_discovery.py new file mode 100644 index 0000000..0c22bd3 --- /dev/null +++ b/packages/relarena-core/tests/test_discovery.py @@ -0,0 +1,84 @@ +"""Installed model modules populate the registry through decorators.""" + +from __future__ import annotations + +from importlib import import_module +from types import SimpleNamespace +from unittest.mock import Mock + +import pytest + +from relarena_core import discovery +from relarena_core.model import RelArenaModel +from relarena_core.registry import MethodRegistry, register_model +from relarena_core.search_space import SearchSpace + +registry_module = import_module("relarena_core.registry") + + +@pytest.fixture +def isolated(monkeypatch: pytest.MonkeyPatch) -> MethodRegistry: + registry = MethodRegistry() + monkeypatch.setattr(registry_module, "registry", registry) + monkeypatch.setattr(discovery, "_loaded", set()) + return registry + + +def _entry(name: str, load: Mock) -> SimpleNamespace: + return SimpleNamespace(name=name, value=f"{name}.model", load=load) + + +def test_successful_module_import_runs_once( + isolated: MethodRegistry, monkeypatch: pytest.MonkeyPatch +) -> None: + def import_model() -> None: + register_model(search_space=SearchSpace(default_overrides={}))( + type("Example", (RelArenaModel,), {"name": "example"}) + ) + + entry = _entry("example", Mock(side_effect=import_model)) + entries = Mock(return_value=[entry]) + monkeypatch.setattr(discovery, "entry_points", entries) + discovery.discover_models() + discovery.discover_models() + assert isolated.names() == ["example"] + entry.load.assert_called_once_with() + entries.assert_called_with(group="relarena.models") + + +def test_failed_plugin_is_visible_and_retryable( + isolated: MethodRegistry, monkeypatch: pytest.MonkeyPatch +) -> None: + missing = ModuleNotFoundError("plugin backend is absent", name="example_backend") + entry = _entry("broken", Mock(side_effect=[missing, None])) + monkeypatch.setattr(discovery, "entry_points", Mock(return_value=[entry])) + with pytest.raises(RuntimeError, match="broken.*broken.model") as error: + discovery.discover_models() + assert error.value.__cause__ is missing + discovery.discover_models() + discovery.discover_models() + assert entry.load.call_count == 2 + + +def test_duplicate_model_name_is_not_silently_replaced( + isolated: MethodRegistry, monkeypatch: pytest.MonkeyPatch +) -> None: + first = type("First", (RelArenaModel,), {"name": "same"}) + second = type("Second", (RelArenaModel,), {"name": "same"}) + space = SearchSpace(default_overrides={}) + isolated.register(first, space) + entry = _entry( + "collision", + Mock(side_effect=lambda: register_model(search_space=space)(second)), + ) + monkeypatch.setattr(discovery, "entry_points", Mock(return_value=[entry])) + with pytest.raises(RuntimeError, match="collision") as error: + discovery.discover_models() + assert isinstance(error.value.__cause__, ValueError) + assert isolated.get("same") is first + + +def test_missing_model_explains_discovery() -> None: + with pytest.raises(KeyError) as error: + MethodRegistry().get("external-model") + assert "discover_models()" in str(error.value) diff --git a/packages/relarena/tests/core/test_identity.py b/packages/relarena-core/tests/test_identity.py similarity index 98% rename from packages/relarena/tests/core/test_identity.py rename to packages/relarena-core/tests/test_identity.py index 80085a3..367765c 100644 --- a/packages/relarena/tests/core/test_identity.py +++ b/packages/relarena-core/tests/test_identity.py @@ -7,7 +7,7 @@ import pandas as pd from relbench.base import Database, Table -from relarena.core.identity import ( +from relarena_core.identity import ( RunIdentity, database_schema_fingerprint, metadata_fingerprint, diff --git a/packages/relarena/tests/core/test_metrics.py b/packages/relarena-core/tests/test_metrics.py similarity index 99% rename from packages/relarena/tests/core/test_metrics.py rename to packages/relarena-core/tests/test_metrics.py index 8739244..fbdb5f2 100644 --- a/packages/relarena/tests/core/test_metrics.py +++ b/packages/relarena-core/tests/test_metrics.py @@ -10,7 +10,7 @@ from relbench.base import TaskType from relbench.metrics import roc_auc -from relarena.core.metrics import ( +from relarena_core.metrics import ( _METRICS, Metric, evaluate_predictions, diff --git a/packages/relarena/tests/core/test_model.py b/packages/relarena-core/tests/test_model.py similarity index 88% rename from packages/relarena/tests/core/test_model.py rename to packages/relarena-core/tests/test_model.py index 6d94ab1..c781025 100644 --- a/packages/relarena/tests/core/test_model.py +++ b/packages/relarena-core/tests/test_model.py @@ -6,8 +6,8 @@ import numpy as np -from relarena.core.cache import CacheConfig -from relarena.core.model import RelArenaModel +from relarena_core.cache import CacheConfig +from relarena_core.model import RelArenaModel class _Model(RelArenaModel): diff --git a/packages/relarena-core/tests/test_package_boundary.py b/packages/relarena-core/tests/test_package_boundary.py new file mode 100644 index 0000000..f4a249e --- /dev/null +++ b/packages/relarena-core/tests/test_package_boundary.py @@ -0,0 +1,29 @@ +"""Core imports must remain independent of benchmark and model packages.""" + +import subprocess +import sys + + +def test_imports_and_schema_loading_without_consumers() -> None: + code = """ +import importlib.abc +import pkgutil +import sys + +class BlockConsumers(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname.split('.')[0] in {'relarena', 'tabpfn_rel'}: + raise AssertionError(f'Core attempted a consumer import: {fullname}') + return None + +sys.meta_path.insert(0, BlockConsumers()) +import relarena_core +for info in pkgutil.walk_packages(relarena_core.__path__, 'relarena_core.'): + __import__(info.name) +from relarena_core.userdb._schema import load_schema +assert load_schema('database.schema.json')['type'] == 'object' +assert load_schema('task.schema.json')['type'] == 'object' +assert not relarena_core.registry.names() +assert not any(n == 'relarena' or n.startswith('relarena.') for n in sys.modules) +""" + subprocess.run([sys.executable, "-c", code], check=True) diff --git a/packages/relarena/tests/core/test_predict_contract.py b/packages/relarena-core/tests/test_predict_contract.py similarity index 96% rename from packages/relarena/tests/core/test_predict_contract.py rename to packages/relarena-core/tests/test_predict_contract.py index 2ba1176..498581f 100644 --- a/packages/relarena/tests/core/test_predict_contract.py +++ b/packages/relarena-core/tests/test_predict_contract.py @@ -10,7 +10,7 @@ import numpy as np from relbench.base import TaskType -from relarena.core.predict_contract import predict_to_contract +from relarena_core.predict_contract import predict_to_contract class _StubClassifier: diff --git a/packages/relarena/tests/core/test_search_space.py b/packages/relarena-core/tests/test_search_space.py similarity index 95% rename from packages/relarena/tests/core/test_search_space.py rename to packages/relarena-core/tests/test_search_space.py index 643afe5..9448fe2 100644 --- a/packages/relarena/tests/core/test_search_space.py +++ b/packages/relarena-core/tests/test_search_space.py @@ -2,7 +2,7 @@ from __future__ import annotations -from relarena.core.search_space import ( +from relarena_core.search_space import ( SearchSpace, TaskStats, resolve_search_space, diff --git a/packages/relarena/tests/core/test_skeleton.py b/packages/relarena-core/tests/test_skeleton.py similarity index 90% rename from packages/relarena/tests/core/test_skeleton.py rename to packages/relarena-core/tests/test_skeleton.py index 9cb1372..e088df9 100644 --- a/packages/relarena/tests/core/test_skeleton.py +++ b/packages/relarena-core/tests/test_skeleton.py @@ -5,12 +5,12 @@ import numpy as np import pytest -from relarena.core.metrics import get_metric, is_better, is_higher_better -from relarena.core.model import RelArenaModel -from relarena.core.registry import ModelRegistry -from relarena.core.results import config_id_for -from relarena.core.search_space import SearchSpace -from relarena.core.system import RelArenaSystem +from relarena_core.metrics import get_metric, is_better, is_higher_better +from relarena_core.model import RelArenaModel +from relarena_core.registry import ModelRegistry +from relarena_core.results import config_id_for +from relarena_core.search_space import SearchSpace +from relarena_core.system import RelArenaSystem def test_metric_direction() -> None: diff --git a/packages/relarena-core/tests/test_standalone_runtime.py b/packages/relarena-core/tests/test_standalone_runtime.py new file mode 100644 index 0000000..4d4ea0b --- /dev/null +++ b/packages/relarena-core/tests/test_standalone_runtime.py @@ -0,0 +1,110 @@ +"""Tuned predictive queries with a supplied model and no benchmark integration.""" + +from pathlib import Path + +import numpy as np +import pandas as pd +import pytest +from relbench.base import Database, EntityTask, Table + +from relarena_core import RelArenaModel, registry +from relarena_core.search_space import SearchSpace +from relarena_core.userdb import PredictiveQuery, PredictiveQuerySpec +from relarena_core.userdb import query as query_module + + +@pytest.mark.parametrize("refit_full", [False, True]) +def test_tuning_final_fit_and_original_ids( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, refit_full: bool +) -> None: + customers = pd.DataFrame({"customer_id": ["a", "b", "c", "d"]}) + dates = pd.date_range("2004-01-15", "2005-06-15", freq="30D") + events = pd.DataFrame( + [ + {"customer_id": customer, "date": date} + for i, date in enumerate(dates) + for j, customer in enumerate(customers.customer_id) + if (i + j) % 2 == 0 + ] + ) + customers.to_parquet(tmp_path / "customers.parquet") + events.assign(event_id=range(len(events))).to_parquet(tmp_path / "events.parquet") + (tmp_path / "database.yaml").write_text( + "customers:\n pkey: customer_id\n" + "events:\n pkey: event_id\n time_col: date\n" + " fkeys:\n customer_id: customers\n" + ) + (tmp_path / "task.yaml").write_text("""database: database.yaml +entity_table: customers +entity_col: customer_id +time_col: date +target_col: y +task_type: binary_classification +timedelta: 30 days +val_timestamp: '2004-10-01' +test_timestamp: '2004-12-01' +query: | + SELECT t.timestamp AS date, c.customer_id, + CAST(COUNT(e.event_id) > 0 AS INTEGER) AS y + FROM timestamp_df t CROSS JOIN customers c + LEFT JOIN events e ON e.customer_id = c.customer_id + AND e.date > t.timestamp + AND e.date <= t.timestamp + INTERVAL '{timedelta}' + GROUP BY t.timestamp, c.customer_id +""") + fits = [] + + class SuppliedModel(RelArenaModel): + name = "test-standalone" + refit_on_full_data = refit_full + + def fit( + self, + task: EntityTask, + db: Database, + train_table: Table, + val_table: Table | None, + *, + seed: int, + time_limit: float | None = None, + ) -> None: + if self.config["fail"]: + raise ValueError("Deliberate failed tuning candidate") + self.mean = float(train_table.df[task.target_col].mean()) + fits.append( + ( + len(train_table.df), + val_table is None, + db.table_dict["events"].df.date.max(), + ) + ) + + def predict(self, task: EntityTask, db: Database, table: Table) -> np.ndarray: + return np.full(len(table.df), self.mean) + + monkeypatch.setattr(query_module, "discover_models", lambda: None) + monkeypatch.setattr(registry, "_entries", {}) + registry.register( + SuppliedModel, + SearchSpace( + default_overrides={"fail": False}, + fixed_grid=[{"fail": True}, {"fail": False}], + ), + ) + spec = PredictiveQuerySpec.from_yaml(tmp_path / "task.yaml", data_dir=tmp_path) + query = PredictiveQuery(spec, data_version="fixture-v1") + inner, outer = query._source.inner_split(), query._source.outer_split() + query.fit(SuppliedModel.name, n_trials=2) + assert query.config == {"fail": False} + assert [trial.ok for trial in query.trials] == [False, True] + assert fits[0][0] == len(inner.train_table.df) + assert fits[0][2] <= inner.cutoff + expected_rows = len(outer.train_table.df) + if refit_full: + expected_rows += len(outer.val_table.df) + assert fits[-1][:2] == (expected_rows, refit_full) + assert fits[-1][2] <= outer.cutoff + predictions = query.predict() + assert sorted(predictions.customer_id) == ["a", "b", "c", "d"] + assert np.isfinite(predictions.y_pred).all() + assert len(query.compute_test_labels()) == 4 diff --git a/packages/relarena/tests/core/test_tfm.py b/packages/relarena-core/tests/test_tfm.py similarity index 98% rename from packages/relarena/tests/core/test_tfm.py rename to packages/relarena-core/tests/test_tfm.py index 97833c1..a351c6b 100644 --- a/packages/relarena/tests/core/test_tfm.py +++ b/packages/relarena-core/tests/test_tfm.py @@ -7,7 +7,7 @@ import pytest from relbench.base import TaskType -from relarena.core.tfm import TFMSpec, _downsample_indices, fit_tfm, predict_tfm +from relarena_core.tfm import TFMSpec, _downsample_indices, fit_tfm, predict_tfm class _StubClassifier: diff --git a/packages/relarena/tests/core/test_tuner.py b/packages/relarena-core/tests/test_tuner.py similarity index 96% rename from packages/relarena/tests/core/test_tuner.py rename to packages/relarena-core/tests/test_tuner.py index 7ed70d8..2c25aee 100644 --- a/packages/relarena/tests/core/test_tuner.py +++ b/packages/relarena-core/tests/test_tuner.py @@ -5,8 +5,8 @@ import pytest from ConfigSpace import ConfigurationSpace, Integer -from relarena.core.search_space import SearchSpace -from relarena.core.tuner import _concise_error, plan_configs +from relarena_core.search_space import SearchSpace +from relarena_core.tuner import _concise_error, plan_configs def _random_space() -> SearchSpace: diff --git a/packages/relarena/tests/core/userdb/test_ingest.py b/packages/relarena-core/tests/userdb/test_ingest.py similarity index 98% rename from packages/relarena/tests/core/userdb/test_ingest.py rename to packages/relarena-core/tests/userdb/test_ingest.py index 7148f59..2336770 100644 --- a/packages/relarena/tests/core/userdb/test_ingest.py +++ b/packages/relarena-core/tests/userdb/test_ingest.py @@ -7,7 +7,7 @@ import pandas as pd import pytest -from relarena.core.userdb.ingest import DatabaseSpec, TableSource, build_dataset +from relarena_core.userdb.ingest import DatabaseSpec, TableSource, build_dataset def test__build_dataset__pkey_maps__capture_original_to_reindexed_ids( diff --git a/packages/relarena/tests/core/userdb/test_predict.py b/packages/relarena-core/tests/userdb/test_predict.py similarity index 97% rename from packages/relarena/tests/core/userdb/test_predict.py rename to packages/relarena-core/tests/userdb/test_predict.py index 167ea02..b6434e0 100644 --- a/packages/relarena/tests/core/userdb/test_predict.py +++ b/packages/relarena-core/tests/userdb/test_predict.py @@ -8,7 +8,7 @@ import pytest from relbench.base import Database, Table -from relarena.core.userdb.predict import make_prediction_table +from relarena_core.userdb.predict import make_prediction_table def test__make_prediction_table__explicit_ids__drops_absent_entities() -> None: diff --git a/packages/relarena/tests/core/userdb/test_task.py b/packages/relarena-core/tests/userdb/test_task.py similarity index 98% rename from packages/relarena/tests/core/userdb/test_task.py rename to packages/relarena-core/tests/userdb/test_task.py index 2cd7d14..8def6e2 100644 --- a/packages/relarena/tests/core/userdb/test_task.py +++ b/packages/relarena-core/tests/userdb/test_task.py @@ -6,7 +6,7 @@ import pytest from relbench.base import Database, Table -from relarena.core.userdb.task import UserEntityTask +from relarena_core.userdb.task import UserEntityTask def _drivers_db() -> Database: diff --git a/packages/relarena/pyproject.toml b/packages/relarena/pyproject.toml index 9382e89..f0a44eb 100644 --- a/packages/relarena/pyproject.toml +++ b/packages/relarena/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "relarena" -version = "0.0.1" +version = "0.0.3" authors = [{ name = "Prior Labs" }] license = "Apache-2.0" license-files = ["LICENSE", "NOTICE", "src/relarena/models/VENDORED-LICENSES"] @@ -27,6 +27,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: Artificial Intelligence", ] dependencies = [ + "relarena-core==0.0.1", # Pinned exactly: RelBench ships the dataset/task SHA256 checksums in the package # itself (relbench/datasets/hashes.json, relbench/tasks/hashes.json, wired into the # pooch registries in relbench/datasets/__init__.py and relbench/tasks/__init__.py), @@ -71,11 +72,11 @@ lightgbm = ["lightgbm>=4.0,<4.7"] # native system entry because selection happens inside its end-to-end run. kurversc = ["kurversc==0.1.1"] # rdblearn: DFS features + a tabular foundation model. Carries the local TFM -# (tabpfn>=8 for the create_default_for_version API, lazily imported in models/_tfm.py) -# plus the DFS featurization deps; `tabpfn-rel-local` uses this same extra. +# (tabpfn>=8 for the create_default_for_version API, lazily imported in relarena.models.rdblearn.tfm) +# plus the DFS featurization deps. # setuptools<82 because featuretools -> woodwork imports pkg_resources at import # time, and setuptools 82 removed it (81 still ships it). -rdblearn = ["tabpfn>=8", "fastdfs>=1.1", "setuptools<82"] +rdblearn = ["tabpfn>=8", "relarena-core[dfs]==0.0.1"] # rdl: the shared Relational Deep Learning stack for the GNN baselines — PyG + PyTorch # Frame + a text embedder, which relbench.modeling.* needs (all lazy-imported inside # fit()). GPU recommended. NOTE: PyG neighbor sampling needs `pyg-lib` or `torch-sparse` @@ -89,10 +90,7 @@ rdl = ["torch-geometric>=2.5", "pytorch-frame>=0.2.3", "sentence-transformers"] graphsage = ["relarena[rdl]"] relgnn = ["relarena[rdl]"] relgt = ["relarena[rdl]", "einops>=0.8", "h5py>=3.0"] -# tabpfn-rel-local runs the local TabPFN v3 over DFS features — the same stack as -# rdblearn, exposed under the model's own name. tabpfn-rel-api covers -# tabpfn-rel-client (the tabpfn-v3-api TFM): DFS locally, fit/predict server-side, -# text embedded natively by the API — no GPU and no local tabpfn needed. +# TabPFN-Rel backends are optional; the model registers with the built-in models. tabpfn-rel-local = ["relarena[rdblearn]"] tabpfn-rel-api = ["fastdfs>=1.1", "setuptools<82", "tabpfn-client>=0.3.2"] # RelArena supports RT execution on Linux x86_64, for which relational-transformer @@ -108,3 +106,7 @@ leaderboard = ["bencheval"] # matplotlib/seaborn/plotly stack and the CD plot; `autorank` runs the Demšar # significance test. plots = ["bencheval[plot]", "autorank"] + + +[project.entry-points."relarena.models"] +relarena = "relarena.models" diff --git a/packages/relarena/src/relarena/__init__.py b/packages/relarena/src/relarena/__init__.py index 3519264..f6ced0f 100644 --- a/packages/relarena/src/relarena/__init__.py +++ b/packages/relarena/src/relarena/__init__.py @@ -16,20 +16,6 @@ split_checksums, table_checksum, ) -from relarena.core.cache import CacheConfig, CacheMiss, cache_key, cached_artifact -from relarena.core.dataset import InnerSplit, OuterSplit, Split -from relarena.core.identity import RunIdentity -from relarena.core.model import RelArenaModel -from relarena.core.registry import ( - MethodRegistry, - ModelRegistry, - register_model, - register_system, - registry, -) -from relarena.core.results import SystemResult, TrialResult -from relarena.core.system import RelArenaSystem -from relarena.core.tuner import tune from relarena.dataset import RelBenchDatasetTask from relarena.results import summary_to_dataframe from relarena.runner import ( @@ -38,6 +24,21 @@ run_system_experiment, ) from relarena.tasks import RELBENCH_V1_DATASETS, TaskSpec, list_entity_tasks +from relarena_core.cache import CacheConfig, CacheMiss, cache_key, cached_artifact +from relarena_core.dataset import InnerSplit, OuterSplit, Split +from relarena_core.discovery import discover_models +from relarena_core.identity import RunIdentity +from relarena_core.model import RelArenaModel +from relarena_core.registry import ( + MethodRegistry, + ModelRegistry, + register_model, + register_system, + registry, +) +from relarena_core.results import SystemResult, TrialResult +from relarena_core.system import RelArenaSystem +from relarena_core.tuner import tune __all__ = [ "RELBENCH_V1_DATASETS", @@ -58,6 +59,7 @@ "TaskSpec", "TrialResult", "SystemResult", + "discover_models", "cache_key", "cached_artifact", "list_entity_tasks", @@ -70,4 +72,4 @@ "database_checksum", "split_checksums", ] -__version__ = "0.0.1" +__version__ = "0.0.3" diff --git a/packages/relarena/src/relarena/checksums/checksum.py b/packages/relarena/src/relarena/checksums/checksum.py index af72954..03271e7 100644 --- a/packages/relarena/src/relarena/checksums/checksum.py +++ b/packages/relarena/src/relarena/checksums/checksum.py @@ -24,8 +24,8 @@ from relbench.base import EntityTask, Table -from relarena.core.fingerprints import array_checksum, database_checksum, table_checksum from relarena.dataset import RelBenchDatasetTask, drop_noncanonical_task_columns +from relarena_core.fingerprints import array_checksum, database_checksum, table_checksum #: Recorded baseline, shipped as package data beside this module. CHECKSUMS_PATH = Path(__file__).with_name("relbench_v1_checksums.json") diff --git a/packages/relarena/src/relarena/cli.py b/packages/relarena/src/relarena/cli.py index c08af4a..8ed38eb 100644 --- a/packages/relarena/src/relarena/cli.py +++ b/packages/relarena/src/relarena/cli.py @@ -19,14 +19,14 @@ import argparse import sys -from importlib import import_module import pandas as pd -from relarena.core.registry import registry from relarena.results import summary_to_dataframe from relarena.runner import SystemExperimentSummary, run_experiment from relarena.tasks import RELBENCH_V1_DATASETS, list_entity_tasks +from relarena_core.discovery import discover_models +from relarena_core.registry import registry def _build_parser() -> argparse.ArgumentParser: @@ -88,7 +88,7 @@ def main(argv: list[str] | None = None) -> int: print("Nothing to run.", file=sys.stderr) return 1 - import_module("relarena.models") + discover_models() try: model_cls = registry.get(args.model) except KeyError as exc: diff --git a/packages/relarena/src/relarena/dataset.py b/packages/relarena/src/relarena/dataset.py index 5511073..f93444d 100644 --- a/packages/relarena/src/relarena/dataset.py +++ b/packages/relarena/src/relarena/dataset.py @@ -28,11 +28,11 @@ from relbench.datasets import get_dataset from relbench.tasks import get_task -from relarena.core.dataset import ( +from relarena.identity import relbench_run_identity +from relarena_core.dataset import ( TaskSource, clean_database, ) -from relarena.identity import relbench_run_identity #: rel-ratebeer per-user aggregates computed over the *entire* rating history, so a #: row can encode information from after its own timestamp. diff --git a/packages/relarena/src/relarena/evaluation/leaderboard.py b/packages/relarena/src/relarena/evaluation/leaderboard.py index 86faa63..67b320f 100644 --- a/packages/relarena/src/relarena/evaluation/leaderboard.py +++ b/packages/relarena/src/relarena/evaluation/leaderboard.py @@ -17,13 +17,13 @@ from __future__ import annotations import logging -from importlib import import_module import pandas as pd -from relarena.core.metrics import to_metric_error -from relarena.core.registry import registry from relarena.evaluation.subsets import TaskMask, apply_subset +from relarena_core.discovery import discover_models +from relarena_core.metrics import to_metric_error +from relarena_core.registry import registry logger = logging.getLogger(__name__) @@ -115,7 +115,7 @@ def method_kind(model: str) -> str: package rather than isolating one model (see `RelArenaSystem`). Unregistered names (reference baselines, retired methods) rank as models. """ - import_module("relarena.models") + discover_models() try: return registry.kind(model) diff --git a/packages/relarena/src/relarena/featurization/entity.py b/packages/relarena/src/relarena/featurization/entity.py index 5a14f2f..ac84029 100644 --- a/packages/relarena/src/relarena/featurization/entity.py +++ b/packages/relarena/src/relarena/featurization/entity.py @@ -15,7 +15,7 @@ import pandas as pd from relbench.base import Database, EntityTask, Table -from relarena.core.featurization.columns import type_columns +from relarena_core.featurization.columns import type_columns def build_entity_features( diff --git a/packages/relarena/src/relarena/featurization/warm_cache.py b/packages/relarena/src/relarena/featurization/warm_cache.py index e8687e5..e17ee45 100644 --- a/packages/relarena/src/relarena/featurization/warm_cache.py +++ b/packages/relarena/src/relarena/featurization/warm_cache.py @@ -5,10 +5,10 @@ import argparse from pathlib import Path -from relarena.core.cache import resolve_cache_config -from relarena.core.featurization.dfs import DFS_MAX_DEPTH -from relarena.core.featurization.warm_cache import warm_dfs_cache from relarena.dataset import RelBenchDatasetTask +from relarena_core.cache import resolve_cache_config +from relarena_core.featurization.dfs import DFS_MAX_DEPTH +from relarena_core.featurization.warm_cache import warm_dfs_cache def main(argv: list[str] | None = None) -> int: diff --git a/packages/relarena/src/relarena/identity.py b/packages/relarena/src/relarena/identity.py index c062783..df899cf 100644 --- a/packages/relarena/src/relarena/identity.py +++ b/packages/relarena/src/relarena/identity.py @@ -4,7 +4,7 @@ from functools import cache from pathlib import Path -from relarena.core.identity import ( +from relarena_core.identity import ( RunIdentity, metadata_fingerprint, ) diff --git a/packages/relarena/src/relarena/models/dummy/model.py b/packages/relarena/src/relarena/models/dummy/model.py index dafd6a1..7a7e34f 100644 --- a/packages/relarena/src/relarena/models/dummy/model.py +++ b/packages/relarena/src/relarena/models/dummy/model.py @@ -30,11 +30,11 @@ from relbench.base import Database, EntityTask, Table, TaskType from sklearn.dummy import DummyClassifier, DummyRegressor -from relarena.core.metrics import primary_metric -from relarena.core.model import RelArenaModel -from relarena.core.predict_contract import predict_to_contract -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace +from relarena_core.metrics import primary_metric +from relarena_core.model import RelArenaModel +from relarena_core.predict_contract import predict_to_contract +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace #: Metrics minimized by the median; everything else (MSE/RMSE/R²) by the mean. _MEDIAN_METRICS = {"mae"} diff --git a/packages/relarena/src/relarena/models/graphsage/model.py b/packages/relarena/src/relarena/models/graphsage/model.py index ec6c45a..85e8c3d 100644 --- a/packages/relarena/src/relarena/models/graphsage/model.py +++ b/packages/relarena/src/relarena/models/graphsage/model.py @@ -57,10 +57,6 @@ from ConfigSpace import Categorical, ConfigurationSpace, Float from relbench.base import Database, EntityTask, Table, TaskType -from relarena.core.metrics import get_metric, primary_metric -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace from relarena.models._shared.gnn.graph import GRAPH_CACHE, build_graph from relarena.models._shared.gnn.training import ( default_device, @@ -68,6 +64,10 @@ task_setup, train_epoch, ) +from relarena_core.metrics import get_metric, primary_metric +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace logger = logging.getLogger(__name__) diff --git a/packages/relarena/src/relarena/models/kurversc/model.py b/packages/relarena/src/relarena/models/kurversc/model.py index 587506c..4f0e629 100644 --- a/packages/relarena/src/relarena/models/kurversc/model.py +++ b/packages/relarena/src/relarena/models/kurversc/model.py @@ -28,9 +28,9 @@ import numpy as np from relbench.base import Database, EntityTask, Table -from relarena.core.dataset import InnerSplit, OuterSplit -from relarena.core.registry import register_system -from relarena.core.system import RelArenaSystem +from relarena_core.dataset import InnerSplit, OuterSplit +from relarena_core.registry import register_system +from relarena_core.system import RelArenaSystem logger = logging.getLogger(__name__) diff --git a/packages/relarena/src/relarena/models/lightgbm/model.py b/packages/relarena/src/relarena/models/lightgbm/model.py index a27f12e..3e7e297 100644 --- a/packages/relarena/src/relarena/models/lightgbm/model.py +++ b/packages/relarena/src/relarena/models/lightgbm/model.py @@ -31,11 +31,11 @@ from ConfigSpace import Categorical, ConfigurationSpace, Constant, Float, Integer from relbench.base import Database, EntityTask, Table -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace from relarena.featurization import build_entity_features from relarena.models._shared.gbdt.lgb import fit_lgb, predict_lgb +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace def _lightgbm_config_space() -> ConfigurationSpace: diff --git a/packages/relarena/src/relarena/models/rdblearn/model.py b/packages/relarena/src/relarena/models/rdblearn/model.py index 69d3893..594f2c1 100644 --- a/packages/relarena/src/relarena/models/rdblearn/model.py +++ b/packages/relarena/src/relarena/models/rdblearn/model.py @@ -2,7 +2,7 @@ Combines: * **featurization** — multi-hop Deep Feature Synthesis over the foreign-key graph - (`relarena.core.featurization.build_dfs_features`, with the depth cache), + (`relarena_core.featurization.build_dfs_features`, with the depth cache), plus target-history augmentation (past-label aggregates), temporal-diff features, and the anchor columns (entity key + cutoff-time calendar features); * **search space** — an explicit grid over **(which tabular foundation model) × @@ -13,7 +13,7 @@ The estimator is a tabular foundation model (TabPFN v2 / v2.5); see `relarena.models.rdblearn.tfm` for the backend definitions and -`relarena.core.tfm` for shared fitting +`relarena_core.tfm` for shared fitting (the TFM handles categoricals natively). This is RDBLearn proper (https://github.com/HKUSHXLab/rdblearn) — DFS features + a foundation model. @@ -42,12 +42,12 @@ import numpy as np from relbench.base import Database, EntityTask, Table -from relarena.core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace -from relarena.core.tfm import fit_tfm, predict_tfm from relarena.models.rdblearn.tfm import TFM_REGISTRY +from relarena_core.featurization.dfs import DFS_MAX_DEPTH, build_dfs_features +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace +from relarena_core.tfm import fit_tfm, predict_tfm _MIN_DEPTH = 2 diff --git a/packages/relarena/src/relarena/models/rdblearn/tfm.py b/packages/relarena/src/relarena/models/rdblearn/tfm.py index 1996b47..669490d 100644 --- a/packages/relarena/src/relarena/models/rdblearn/tfm.py +++ b/packages/relarena/src/relarena/models/rdblearn/tfm.py @@ -4,7 +4,7 @@ from typing import Any -from relarena.core.tfm import TFMSpec +from relarena_core.tfm import TFMSpec def _make_tabpfn( diff --git a/packages/relarena/src/relarena/models/relgnn/model.py b/packages/relarena/src/relarena/models/relgnn/model.py index 8d7da1f..d8bcb35 100644 --- a/packages/relarena/src/relarena/models/relgnn/model.py +++ b/packages/relarena/src/relarena/models/relgnn/model.py @@ -42,11 +42,6 @@ from ConfigSpace import Categorical, ConfigurationSpace, Float from relbench.base import Database, EntityTask, Table -from relarena.core.metrics import get_metric, primary_metric -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace -from relarena.core.tasks import ENTITY_TASK_TYPES from relarena.models._shared.gnn.training import ( default_device, infer, @@ -54,6 +49,11 @@ train_epoch, ) from relarena.models.relgnn.preprocessing import load_graph +from relarena_core.metrics import get_metric, primary_metric +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace +from relarena_core.tasks import ENTITY_TASK_TYPES # Fixed budget / infra constants (never tuned; not part of any hyperparameter config). _BATCH_SIZE = 512 diff --git a/packages/relarena/src/relarena/models/relgnn/preprocessing.py b/packages/relarena/src/relarena/models/relgnn/preprocessing.py index 7733609..365fec3 100644 --- a/packages/relarena/src/relarena/models/relgnn/preprocessing.py +++ b/packages/relarena/src/relarena/models/relgnn/preprocessing.py @@ -8,10 +8,10 @@ from relbench.base import Database -from relarena.core.cache import CacheConfig, cache_key, cached_artifact -from relarena.core.fingerprints import database_checksum -from relarena.core.identity import RunIdentity from relarena.models._shared.gnn.graph_cache import DBGraphCache +from relarena_core.cache import CacheConfig, cache_key, cached_artifact +from relarena_core.fingerprints import database_checksum +from relarena_core.identity import RunIdentity _GRAPH_CACHE_VERSION = 2 _TEXT_EMBED_BATCH_SIZE = 256 diff --git a/packages/relarena/src/relarena/models/relgnn/warm_cache.py b/packages/relarena/src/relarena/models/relgnn/warm_cache.py index 375e6b9..b3b164c 100644 --- a/packages/relarena/src/relarena/models/relgnn/warm_cache.py +++ b/packages/relarena/src/relarena/models/relgnn/warm_cache.py @@ -7,9 +7,9 @@ import torch -from relarena.core.cache import resolve_cache_config from relarena.dataset import RelBenchDatasetTask from relarena.models.relgnn.preprocessing import load_graph +from relarena_core.cache import resolve_cache_config def precompute_dataset_task( diff --git a/packages/relarena/src/relarena/models/relgt/model.py b/packages/relarena/src/relarena/models/relgt/model.py index 3ee7c2a..92ddd95 100644 --- a/packages/relarena/src/relarena/models/relgt/model.py +++ b/packages/relarena/src/relarena/models/relgt/model.py @@ -37,10 +37,10 @@ from relbench.base import Database, EntityTask, Table, TaskType from torch.utils.data import DataLoader -from relarena.core.metrics import get_metric, primary_metric -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace, TaskStats +from relarena_core.metrics import get_metric, primary_metric +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace, TaskStats # Architecture constants — fixed across the paper's runs (expts/*.sh), not tuned. _CHANNELS = 512 diff --git a/packages/relarena/src/relarena/models/relgt/tokenize.py b/packages/relarena/src/relarena/models/relgt/tokenize.py index 835ed3f..ac48fd2 100644 --- a/packages/relarena/src/relarena/models/relgt/tokenize.py +++ b/packages/relarena/src/relarena/models/relgt/tokenize.py @@ -46,13 +46,13 @@ from torch.utils.data import Dataset from torch_geometric.data import HeteroData -from relarena.core.cache import CacheConfig, cache_key, cached_artifact -from relarena.core.identity import RunIdentity from relarena.models.relgt._vendor._sampler import ( _process_one_seed, build_adjacency_hetero, init_worker_globals, ) +from relarena_core.cache import CacheConfig, cache_key, cached_artifact +from relarena_core.identity import RunIdentity #: Hop label of the random-fallback tokens; one past the 2-hop sampler, so the type/hop #: embeddings are sized for hops {0 (seed), 1, 2, 3 (fallback)}. diff --git a/packages/relarena/src/relarena/models/relgt/warm_cache.py b/packages/relarena/src/relarena/models/relgt/warm_cache.py index a1fe3fb..edc87ee 100644 --- a/packages/relarena/src/relarena/models/relgt/warm_cache.py +++ b/packages/relarena/src/relarena/models/relgt/warm_cache.py @@ -6,9 +6,9 @@ from collections.abc import Callable from pathlib import Path -from relarena.core.cache import resolve_cache_config from relarena.dataset import RelBenchDatasetTask from relarena.models._shared.gnn.graph import build_graph +from relarena_core.cache import resolve_cache_config _NUM_NEIGHBORS = 300 diff --git a/packages/relarena/src/relarena/models/rt/export.py b/packages/relarena/src/relarena/models/rt/export.py index a5173cc..082787f 100644 --- a/packages/relarena/src/relarena/models/rt/export.py +++ b/packages/relarena/src/relarena/models/rt/export.py @@ -85,10 +85,10 @@ import yaml from relbench.base import Database, EntityTask, Table, TaskType -from relarena.core.cache import CacheConfig, CacheMiss, cache_key -from relarena.core.fingerprints import database_checksum, table_checksum -from relarena.core.identity import RunIdentity from relarena.models.rt.config import preprocess_args +from relarena_core.cache import CacheConfig, CacheMiss, cache_key +from relarena_core.fingerprints import database_checksum, table_checksum +from relarena_core.identity import RunIdentity logger = logging.getLogger(__name__) @@ -307,7 +307,7 @@ def _publish_directory( ) -> Path: """Build/publish one directory artifact and return the **path** it lives at. - `relarena.core.cache.cached_artifact` is the shared layer for this, and it does + `relarena_core.cache.cached_artifact` is the shared layer for this, and it does not fit: it returns a *value* that `load` materialized in memory, and builds a miss inside a `TemporaryDirectory` that is deleted before it returns. Both are right for a feature matrix and wrong for this artifact, whose consumer diff --git a/packages/relarena/src/relarena/models/rt/model.py b/packages/relarena/src/relarena/models/rt/model.py index 9b811ba..f2d9ea7 100644 --- a/packages/relarena/src/relarena/models/rt/model.py +++ b/packages/relarena/src/relarena/models/rt/model.py @@ -59,12 +59,12 @@ import numpy as np from relbench.base import Database, EntityTask, Table, TaskType -from relarena.core.dataset import InnerSplit, OuterSplit, concat_tables -from relarena.core.identity import RunIdentity -from relarena.core.registry import register_system -from relarena.core.system import RelArenaSystem from relarena.models.rt import config as cfg from relarena.models.rt.export import TASK_DIR, preprocessed_dir, target_stats +from relarena_core.dataset import InnerSplit, OuterSplit, concat_tables +from relarena_core.identity import RunIdentity +from relarena_core.registry import register_system +from relarena_core.system import RelArenaSystem logger = logging.getLogger(__name__) @@ -357,7 +357,7 @@ def _tune_context( from rt.data import get_tasks from rt.eval import build_evaluator - from relarena.core.metrics import is_better, primary_metric + from relarena_core.metrics import is_better, primary_metric metric = primary_metric(task) truth = val_table.df[task.target_col].to_numpy() diff --git a/packages/relarena/src/relarena/models/rt/warm_cache.py b/packages/relarena/src/relarena/models/rt/warm_cache.py index 2f449d6..7f772ce 100644 --- a/packages/relarena/src/relarena/models/rt/warm_cache.py +++ b/packages/relarena/src/relarena/models/rt/warm_cache.py @@ -18,11 +18,11 @@ import argparse from pathlib import Path -from relarena.core.cache import resolve_cache_config -from relarena.core.dataset import concat_tables from relarena.dataset import RelBenchDatasetTask from relarena.models.rt.export import preprocessed_dir from relarena.models.rt.model import DB_NAME +from relarena_core.cache import resolve_cache_config +from relarena_core.dataset import concat_tables def precompute_dataset_task( diff --git a/packages/relarena/src/relarena/models/tabpfn_rel/context.py b/packages/relarena/src/relarena/models/tabpfn_rel/context.py index 446d426..b7f3627 100644 --- a/packages/relarena/src/relarena/models/tabpfn_rel/context.py +++ b/packages/relarena/src/relarena/models/tabpfn_rel/context.py @@ -28,8 +28,8 @@ import pandas as pd from relbench.base import TaskType -from relarena.core.tfm import FittedTFM, fit_tfm from relarena.models.tabpfn_rel.tfm import TFM_REGISTRY +from relarena_core.tfm import FittedTFM, fit_tfm #: Default ensemble size for the pool strategies when the config pins none. DEFAULT_POOL_N_ESTIMATORS = 8 diff --git a/packages/relarena/src/relarena/models/tabpfn_rel/model.py b/packages/relarena/src/relarena/models/tabpfn_rel/model.py index 3bd3e4e..68bf2c8 100644 --- a/packages/relarena/src/relarena/models/tabpfn_rel/model.py +++ b/packages/relarena/src/relarena/models/tabpfn_rel/model.py @@ -16,7 +16,7 @@ in-context examples from a recency-weighted pool instead of a uniform sample. `fit` is the whole story top to bottom: build DFS features for the full train labels -(cached on a warm run; see `relarena.core.featurization.cache`), apply the +(cached on a warm run; see `relarena_core.featurization.cache`), apply the enabled feature extras, then fit the TFM — a seeded downsample for the default `random` context, or the recency pool for `soft_pool` / `hard_pool`. The expensive DFS matrix is content-cached, so the downsample / pool selection happens cheaply @@ -36,14 +36,14 @@ import numpy as np from relbench.base import Database, EntityTask, Table -from relarena.core.featurization import DFS_MAX_DEPTH, build_dfs_features -from relarena.core.model import RelArenaModel -from relarena.core.registry import register_model -from relarena.core.search_space import SearchSpace -from relarena.core.tfm import predict_tfm from relarena.models.tabpfn_rel.context import ContextStrategy from relarena.models.tabpfn_rel.features import FeaturePipeline from relarena.models.tabpfn_rel.tfm import TFM_REGISTRY +from relarena_core.featurization import DFS_MAX_DEPTH, build_dfs_features +from relarena_core.model import RelArenaModel +from relarena_core.registry import register_model +from relarena_core.search_space import SearchSpace +from relarena_core.tfm import predict_tfm #: Depth grid lower bound. Shallower depths yield no DFS features for entity tasks. _MIN_DEPTH = 2 @@ -141,7 +141,7 @@ def warm_cache( Runs exactly the `build_dfs_features` + feature-pipeline calls `fit` and `predict` make (same cache keys), so a later eval reads them instead of recomputing. Construct the model with an explicit fill config; needs only CPU. - The shared public command is `relarena.core.featurization.warm_cache`. + The shared public command is `relarena_core.featurization.warm_cache`. """ self._tfm = self.config.get("tfm", "tabpfn-v3") self._depth = int(self.config.get("max_depth", _MIN_DEPTH)) diff --git a/packages/relarena/src/relarena/models/tabpfn_rel/tfm.py b/packages/relarena/src/relarena/models/tabpfn_rel/tfm.py index 8df900d..9bf204d 100644 --- a/packages/relarena/src/relarena/models/tabpfn_rel/tfm.py +++ b/packages/relarena/src/relarena/models/tabpfn_rel/tfm.py @@ -6,7 +6,7 @@ import numpy as np -from relarena.core.tfm import TFMSpec +from relarena_core.tfm import TFMSpec def _make_tabpfn( diff --git a/packages/relarena/src/relarena/refit.py b/packages/relarena/src/relarena/refit.py index a40f19a..e7b07e6 100644 --- a/packages/relarena/src/relarena/refit.py +++ b/packages/relarena/src/relarena/refit.py @@ -7,11 +7,11 @@ from relbench.base import EntityTask -from relarena.core.cache import CacheConfig -from relarena.core.dataset import OuterSplit, concat_tables -from relarena.core.identity import RunIdentity -from relarena.core.metrics import evaluate_predictions, primary_metric -from relarena.core.model import RelArenaModel +from relarena_core.cache import CacheConfig +from relarena_core.dataset import OuterSplit, concat_tables +from relarena_core.identity import RunIdentity +from relarena_core.metrics import evaluate_predictions, primary_metric +from relarena_core.model import RelArenaModel def refit_and_evaluate( diff --git a/packages/relarena/src/relarena/results.py b/packages/relarena/src/relarena/results.py index 0fc6aea..021d898 100644 --- a/packages/relarena/src/relarena/results.py +++ b/packages/relarena/src/relarena/results.py @@ -6,7 +6,7 @@ from dataclasses import fields from typing import TYPE_CHECKING, Any -from relarena.core.results import SystemResult, TrialResult +from relarena_core.results import SystemResult, TrialResult if TYPE_CHECKING: import pandas as pd diff --git a/packages/relarena/src/relarena/runner.py b/packages/relarena/src/relarena/runner.py index 23e7038..f7d361d 100644 --- a/packages/relarena/src/relarena/runner.py +++ b/packages/relarena/src/relarena/runner.py @@ -17,17 +17,17 @@ import numpy as np from relbench.base import TaskType -from relarena.core.cache import resolve_cache_config -from relarena.core.model import RelArenaModel -from relarena.core.registry import registry -from relarena.core.results import SystemResult, TrialResult -from relarena.core.search_space import SearchSpaceProvider -from relarena.core.selection import select_best -from relarena.core.system import RelArenaSystem -from relarena.core.tasks import ENTITY_TASK_TYPES -from relarena.core.tuner import tune from relarena.dataset import RelBenchDatasetTask from relarena.refit import refit_and_evaluate +from relarena_core.cache import resolve_cache_config +from relarena_core.model import RelArenaModel +from relarena_core.registry import registry +from relarena_core.results import SystemResult, TrialResult +from relarena_core.search_space import SearchSpaceProvider +from relarena_core.selection import select_best +from relarena_core.system import RelArenaSystem +from relarena_core.tasks import ENTITY_TASK_TYPES +from relarena_core.tuner import tune logger = logging.getLogger(__name__) diff --git a/packages/relarena/src/relarena/tasks.py b/packages/relarena/src/relarena/tasks.py index 72af8ee..a314e46 100644 --- a/packages/relarena/src/relarena/tasks.py +++ b/packages/relarena/src/relarena/tasks.py @@ -6,7 +6,7 @@ from relbench.base import TaskType -from relarena.core.tasks import ENTITY_TASK_TYPES as ENTITY_TASK_TYPES +from relarena_core.tasks import ENTITY_TASK_TYPES as ENTITY_TASK_TYPES #: The seven original RelBench (v1) datasets. #: diff --git a/packages/relarena/src/relarena/userdb/__init__.py b/packages/relarena/src/relarena/userdb/__init__.py index d47e7af..006eb60 100644 --- a/packages/relarena/src/relarena/userdb/__init__.py +++ b/packages/relarena/src/relarena/userdb/__init__.py @@ -13,14 +13,14 @@ tasks ship as reference specs (`relbench_v1_spec`). """ -from relarena.core.userdb.ingest import DatabaseSpec -from relarena.core.userdb.query import PredictiveQuery, PredictiveQuerySpec -from relarena.core.userdb.spec import PredictiveTaskSpec from relarena.userdb.relbench_v1 import ( materialize_relbench, relbench_v1_spec, relbench_v1_tasks, ) +from relarena_core.userdb.ingest import DatabaseSpec +from relarena_core.userdb.query import PredictiveQuery, PredictiveQuerySpec +from relarena_core.userdb.spec import PredictiveTaskSpec __all__ = [ "DatabaseSpec", diff --git a/packages/relarena/src/relarena/userdb/relbench_v1/__init__.py b/packages/relarena/src/relarena/userdb/relbench_v1/__init__.py index 9b6542f..3eabd3b 100644 --- a/packages/relarena/src/relarena/userdb/relbench_v1/__init__.py +++ b/packages/relarena/src/relarena/userdb/relbench_v1/__init__.py @@ -24,7 +24,7 @@ from relbench.datasets import get_dataset -from relarena.core.userdb.query import PredictiveQuerySpec +from relarena_core.userdb.query import PredictiveQuerySpec _SPEC_DIR = files(__name__) diff --git a/packages/relarena/tests/fixtures/cached_model.py b/packages/relarena/tests/fixtures/cached_model.py index af35d73..866f9f8 100644 --- a/packages/relarena/tests/fixtures/cached_model.py +++ b/packages/relarena/tests/fixtures/cached_model.py @@ -1,11 +1,11 @@ -"""A novel model-owned cache with formats unknown to relarena.core.cache.""" +"""A novel model-owned cache with formats unknown to relarena_core.cache.""" from __future__ import annotations import hashlib from pathlib import Path -from relarena.core.cache import CacheConfig, cache_key, cached_artifact +from relarena_core.cache import CacheConfig, cache_key, cached_artifact _VERSION = 7 diff --git a/packages/relarena/tests/models/dummy/test_model.py b/packages/relarena/tests/models/dummy/test_model.py index 6a3043b..0f223cd 100644 --- a/packages/relarena/tests/models/dummy/test_model.py +++ b/packages/relarena/tests/models/dummy/test_model.py @@ -13,8 +13,8 @@ import pandas as pd from relbench.base import TaskType -from relarena.core.registry import registry from relarena.models.dummy import DummyBaseline, DummyPerEntityBaseline +from relarena_core.registry import registry def _task( diff --git a/packages/relarena/tests/models/graphsage/test_model.py b/packages/relarena/tests/models/graphsage/test_model.py index 9acea9f..b14d2f9 100644 --- a/packages/relarena/tests/models/graphsage/test_model.py +++ b/packages/relarena/tests/models/graphsage/test_model.py @@ -17,7 +17,6 @@ from ConfigSpace import Configuration from relbench.base import TaskType -from relarena.core.registry import registry from relarena.models.graphsage import model as graphsage_mod from relarena.models.graphsage.model import ( _BATCH_SIZE, @@ -27,6 +26,7 @@ _cuda_cleanup, _run_with_oom_retry, ) +from relarena_core.registry import registry _SUPPORTED_TASK_TYPES = frozenset( { diff --git a/packages/relarena/tests/models/kurversc/test_model.py b/packages/relarena/tests/models/kurversc/test_model.py index 2c208fe..80ff44b 100644 --- a/packages/relarena/tests/models/kurversc/test_model.py +++ b/packages/relarena/tests/models/kurversc/test_model.py @@ -9,10 +9,10 @@ import pytest from relbench.base import Table, TaskType -from relarena.core.identity import RunIdentity -from relarena.core.registry import registry from relarena.models.kurversc import KURVERSC_DEFAULTS, KurveRSCSystem from relarena.tasks import RELBENCH_V1_DATASETS, list_entity_tasks +from relarena_core.identity import RunIdentity +from relarena_core.registry import registry def _label_table(*, masked: bool = False) -> Table: diff --git a/packages/relarena/tests/models/rdblearn/test_model.py b/packages/relarena/tests/models/rdblearn/test_model.py index 57cab78..844b272 100644 --- a/packages/relarena/tests/models/rdblearn/test_model.py +++ b/packages/relarena/tests/models/rdblearn/test_model.py @@ -12,11 +12,11 @@ import pytest from relbench.base import Table, TaskType -from relarena.core.cache import CacheConfig -from relarena.core.registry import registry -from relarena.core.tfm import TFMSpec from relarena.models.rdblearn import RDBLEARN_SPACE, RDBLearnModel, tfm from relarena.models.rdblearn import model as rdblearn +from relarena_core.cache import CacheConfig +from relarena_core.registry import registry +from relarena_core.tfm import TFMSpec def test_registered_under_name_rdblearn() -> None: diff --git a/packages/relarena/tests/models/rdblearn/test_tfm.py b/packages/relarena/tests/models/rdblearn/test_tfm.py index 80a0432..9705ca7 100644 --- a/packages/relarena/tests/models/rdblearn/test_tfm.py +++ b/packages/relarena/tests/models/rdblearn/test_tfm.py @@ -16,7 +16,7 @@ def test__rdblearn_tfm__import__registers_model_without_loading_backends() -> No code = """ import sys from relarena.models.rdblearn import RDBLearnModel, tfm -from relarena.core.registry import registry +from relarena_core.registry import registry prefixes = ('tabpfn', 'tabpfn_client', 'fastdfs') loaded = [name for name in sys.modules diff --git a/packages/relarena/tests/models/relgnn/test_model.py b/packages/relarena/tests/models/relgnn/test_model.py index 4b8840b..104dcb7 100644 --- a/packages/relarena/tests/models/relgnn/test_model.py +++ b/packages/relarena/tests/models/relgnn/test_model.py @@ -18,9 +18,6 @@ from relbench.base import TaskType import relarena.models.relgnn.preprocessing as preprocessing -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity -from relarena.core.registry import registry from relarena.models._shared.gnn.graph_cache import DBGraphCache from relarena.models.relgnn._vendor.atomic_routes import get_atomic_routes from relarena.models.relgnn.model import ( @@ -35,6 +32,9 @@ graph_cache_key, load_graph, ) +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity +from relarena_core.registry import registry # -- atomic routes (pure topology; no heavy deps) --------------------------- diff --git a/packages/relarena/tests/models/relgnn/test_warm_cache.py b/packages/relarena/tests/models/relgnn/test_warm_cache.py index ee40ade..ef00447 100644 --- a/packages/relarena/tests/models/relgnn/test_warm_cache.py +++ b/packages/relarena/tests/models/relgnn/test_warm_cache.py @@ -7,9 +7,9 @@ import pytest -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity from relarena.models.relgnn import warm_cache +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity def test__precompute_dataset_task__fills_inner_and_outer_graphs( diff --git a/packages/relarena/tests/models/relgt/test_model.py b/packages/relarena/tests/models/relgt/test_model.py index 0199a0a..95295b2 100644 --- a/packages/relarena/tests/models/relgt/test_model.py +++ b/packages/relarena/tests/models/relgt/test_model.py @@ -18,8 +18,6 @@ import pytest from relbench.base import TaskType -from relarena.core.registry import registry -from relarena.core.search_space import TaskStats from relarena.models.relgt.model import ( _DEFAULT_CONFIG, _LARGE_NODE_THRESHOLD, @@ -29,6 +27,8 @@ _schedule, relgt_search_space, ) +from relarena_core.registry import registry +from relarena_core.search_space import TaskStats _GRID_COMBOS = {(ly, dr) for ly in (1, 4, 8) for dr in (0.3, 0.4, 0.5)} diff --git a/packages/relarena/tests/models/relgt/test_tokenize.py b/packages/relarena/tests/models/relgt/test_tokenize.py index c4cd84b..c0665ef 100644 --- a/packages/relarena/tests/models/relgt/test_tokenize.py +++ b/packages/relarena/tests/models/relgt/test_tokenize.py @@ -25,14 +25,14 @@ from relbench.modeling.graph import get_node_train_table_input # noqa: E402 from torch_geometric.data import HeteroData # noqa: E402 -from relarena.core.cache import CacheConfig # noqa: E402 -from relarena.core.identity import RunIdentity # noqa: E402 from relarena.models.relgt.tokenize import ( # noqa: E402 RelGTTokens, _require_persistent_identity, precompute_tokens, token_cache_key, ) +from relarena_core.cache import CacheConfig # noqa: E402 +from relarena_core.identity import RunIdentity # noqa: E402 TensorFrame = torch_frame.TensorFrame stype = torch_frame.stype diff --git a/packages/relarena/tests/models/relgt/test_warm_cache.py b/packages/relarena/tests/models/relgt/test_warm_cache.py index b3db582..37ec27c 100644 --- a/packages/relarena/tests/models/relgt/test_warm_cache.py +++ b/packages/relarena/tests/models/relgt/test_warm_cache.py @@ -7,9 +7,9 @@ import pytest -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity from relarena.models.relgt import warm_cache +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity def test__precompute_dataset_task__fills_every_phase_table( diff --git a/packages/relarena/tests/models/rt/test_model.py b/packages/relarena/tests/models/rt/test_model.py index 2cd7f0d..b6592f5 100644 --- a/packages/relarena/tests/models/rt/test_model.py +++ b/packages/relarena/tests/models/rt/test_model.py @@ -18,10 +18,10 @@ import yaml from relbench.base import Table, TaskType -from relarena.core.registry import registry from relarena.models.rt import RTPluRelSystem from relarena.models.rt import config as cfg from relarena.models.rt.export import TASK_DIR, _write_dataset_dir, target_stats +from relarena_core.registry import registry def test__registry__rt_plurel__is_registered_as_a_system() -> None: @@ -568,8 +568,8 @@ def test__embed__identical_text_is_embedded_once_and_linked( import sys from types import ModuleType - from relarena.core.cache import CacheConfig from relarena.models.rt import export + from relarena_core.cache import CacheConfig calls = [] @@ -623,8 +623,8 @@ def test__embed__different_text_is_not_shared( import sys from types import ModuleType - from relarena.core.cache import CacheConfig from relarena.models.rt import export + from relarena_core.cache import CacheConfig calls = [] diff --git a/packages/relarena/tests/models/tabpfn_rel/test_context.py b/packages/relarena/tests/models/tabpfn_rel/test_context.py index 8858c55..7f46260 100644 --- a/packages/relarena/tests/models/tabpfn_rel/test_context.py +++ b/packages/relarena/tests/models/tabpfn_rel/test_context.py @@ -9,7 +9,6 @@ import pytest from relbench.base import TaskType -from relarena.core.tfm import TFMSpec from relarena.models.tabpfn_rel import tfm from relarena.models.tabpfn_rel.context import ( ContextStrategy, @@ -19,6 +18,7 @@ hard_pool_subsample_indices, soft_pool_subsample_indices, ) +from relarena_core.tfm import TFMSpec # -- pool index math --------------------------------------------------------- diff --git a/packages/relarena/tests/models/tabpfn_rel/test_model.py b/packages/relarena/tests/models/tabpfn_rel/test_model.py index 002d3a3..552f978 100644 --- a/packages/relarena/tests/models/tabpfn_rel/test_model.py +++ b/packages/relarena/tests/models/tabpfn_rel/test_model.py @@ -17,9 +17,6 @@ import pytest from relbench.base import Table, TaskType -from relarena.core.cache import CacheConfig -from relarena.core.registry import registry -from relarena.core.tfm import TFMSpec from relarena.models import _register_builtin_models as discover_models from relarena.models.tabpfn_rel import model as model_mod from relarena.models.tabpfn_rel import tfm @@ -31,6 +28,9 @@ TabPFNRelLocalModel, TabPFNRelModel, ) +from relarena_core.cache import CacheConfig +from relarena_core.registry import registry +from relarena_core.tfm import TFMSpec _N = 100 diff --git a/packages/relarena/tests/models/test_discovery.py b/packages/relarena/tests/models/test_discovery.py index 9e2bd39..65864a4 100644 --- a/packages/relarena/tests/models/test_discovery.py +++ b/packages/relarena/tests/models/test_discovery.py @@ -39,7 +39,7 @@ def find_spec(self, fullname, path=None, target=None): return None sys.meta_path.insert(0, BlockBackends()) -from relarena.core.registry import registry +from relarena_core.registry import registry assert not registry.names() import relarena.models assert set(registry.names()) == {_BASELINES!r} @@ -63,9 +63,9 @@ def test_new_model_is_discovered_without_a_catalogue( else: module = package.with_suffix(".py") module.write_text( - "from relarena.core.model import RelArenaModel\n" - "from relarena.core.registry import register_model\n" - "from relarena.core.search_space import SearchSpace\n" + "from relarena_core.model import RelArenaModel\n" + "from relarena_core.registry import register_model\n" + "from relarena_core.search_space import SearchSpace\n" "@register_model(search_space=SearchSpace(default_overrides={}))\n" "class ExtraModel(RelArenaModel):\n" " name = 'extra-model'\n" @@ -77,7 +77,7 @@ def test_new_model_is_discovered_without_a_catalogue( ) code = f""" import relarena.models -from relarena.core.registry import registry +from relarena_core.registry import registry relarena.models.__path__.append({str(tmp_path)!r}) relarena.models._register_builtin_models() from relarena.models.extra_model import ExtraModel diff --git a/packages/relarena/tests/test_cache_extension.py b/packages/relarena/tests/test_cache_extension.py index 910219e..5432b8b 100644 --- a/packages/relarena/tests/test_cache_extension.py +++ b/packages/relarena/tests/test_cache_extension.py @@ -5,7 +5,7 @@ import shutil from pathlib import Path -from relarena.core.cache import CacheConfig +from relarena_core.cache import CacheConfig from tests.fixtures.cached_model import novel_binary, novel_directory, warm_cache diff --git a/packages/relarena/tests/test_cli.py b/packages/relarena/tests/test_cli.py index f62257f..8ef71a0 100644 --- a/packages/relarena/tests/test_cli.py +++ b/packages/relarena/tests/test_cli.py @@ -17,10 +17,10 @@ from relbench.base import TaskType from relarena import cli -from relarena.core.results import SystemResult, TrialResult from relarena.results import summary_to_dataframe from relarena.runner import ExperimentSummary, SystemExperimentSummary from relarena.tasks import TaskSpec +from relarena_core.results import SystemResult, TrialResult def _summary() -> ExperimentSummary: diff --git a/packages/relarena/tests/test_dataset.py b/packages/relarena/tests/test_dataset.py index 15f4bd5..6996a40 100644 --- a/packages/relarena/tests/test_dataset.py +++ b/packages/relarena/tests/test_dataset.py @@ -18,8 +18,8 @@ import pandas as pd from relbench.base import Database, Dataset, Table -from relarena.core.dataset import InnerSplit, OuterSplit from relarena.dataset import RelBenchDatasetTask, drop_noncanonical_columns +from relarena_core.dataset import InnerSplit, OuterSplit #: Five monthly event timestamps; cut the inner split after the 3rd, the outer #: (test) split after the 5th — so censoring at val should drop the last two rows. diff --git a/packages/relarena/tests/test_package_api.py b/packages/relarena/tests/test_package_api.py new file mode 100644 index 0000000..0807da7 --- /dev/null +++ b/packages/relarena/tests/test_package_api.py @@ -0,0 +1,61 @@ +"""Public package APIs share the runtime contracts and model registry.""" + +import ast +from pathlib import Path + +import pytest + +import relarena +import relarena_core +from relarena.userdb import PredictiveQuery +from relarena_core.userdb import PredictiveQuery as CoreQuery + + +@pytest.mark.parametrize( + "name", + [ + "RelArenaModel", + "RelArenaSystem", + "RunIdentity", + "InnerSplit", + "OuterSplit", + "Split", + "MethodRegistry", + "ModelRegistry", + "registry", + "TrialResult", + "SystemResult", + ], +) +def test_public_class_and_registry_identity(name: str) -> None: + assert getattr(relarena, name) is getattr(relarena_core, name) + assert PredictiveQuery is CoreQuery + + +def test_installed_model_registration() -> None: + model = pytest.importorskip("tabpfn_rel") + relarena.discover_models() + relarena.discover_models() + assert relarena_core.registry.get("tabpfn-rel-local") is model.TabPFNRelLocalModel + + +@pytest.mark.parametrize("consumer", ["relarena", "tabpfn_rel"]) +def test_consumers_use_public_core_interfaces(consumer: str) -> None: + package = pytest.importorskip(consumer) + violations = [] + for path in Path(package.__file__).parent.rglob("*.py"): + for node in ast.walk(ast.parse(path.read_text())): + modules = [] + if isinstance(node, ast.ImportFrom) and node.module: + modules = [node.module] + if node.module.startswith("relarena_core"): + if any(name.name.startswith("_") for name in node.names): + violations.append(f"{path}:{node.lineno}: private core name") + elif isinstance(node, ast.Import): + modules = [name.name for name in node.names] + for module in modules: + if module.startswith("relarena_core") and any( + part.startswith("_") for part in module.split(".") + ): + violations.append(f"{path}:{node.lineno}: {module}") + assert not violations, "\n".join(violations) diff --git a/packages/relarena/tests/test_plugin_discovery.py b/packages/relarena/tests/test_plugin_discovery.py new file mode 100644 index 0000000..df64913 --- /dev/null +++ b/packages/relarena/tests/test_plugin_discovery.py @@ -0,0 +1,21 @@ +"""Benchmark integration with model discovery.""" + +import pytest + +from relarena.evaluation import leaderboard +from relarena_core.registry import MethodRegistry +from relarena_core.system import RelArenaSystem + + +def test_leaderboard_discovers_external_system_kind( + monkeypatch: pytest.MonkeyPatch, +) -> None: + isolated = MethodRegistry() + system = type("ExternalSystem", (RelArenaSystem,), {"name": "external-system"}) + + def discover() -> None: + isolated.register_system(system) + + monkeypatch.setattr(leaderboard, "registry", isolated) + monkeypatch.setattr(leaderboard, "discover_models", discover) + assert leaderboard.method_kind("external-system") == "system" diff --git a/packages/relarena/tests/test_refit.py b/packages/relarena/tests/test_refit.py index 5726455..653aff5 100644 --- a/packages/relarena/tests/test_refit.py +++ b/packages/relarena/tests/test_refit.py @@ -10,11 +10,11 @@ import pandas as pd from relbench.base import Table, TaskType -from relarena.core.cache import CacheConfig -from relarena.core.dataset import OuterSplit -from relarena.core.identity import RunIdentity -from relarena.core.model import RelArenaModel from relarena.refit import refit_and_evaluate +from relarena_core.cache import CacheConfig +from relarena_core.dataset import OuterSplit +from relarena_core.identity import RunIdentity +from relarena_core.model import RelArenaModel def _outer_table(entities: list[int], times: list[int], ys: list[float]) -> Table: diff --git a/packages/relarena/tests/test_results.py b/packages/relarena/tests/test_results.py index 4ae41b0..76f85a7 100644 --- a/packages/relarena/tests/test_results.py +++ b/packages/relarena/tests/test_results.py @@ -2,8 +2,8 @@ import numpy as np -from relarena.core.results import TrialResult, config_id_for from relarena.results import trials_to_dataframe +from relarena_core.results import TrialResult, config_id_for def test_trials_to_dataframe_drops_arrays() -> None: diff --git a/packages/relarena/tests/test_runner.py b/packages/relarena/tests/test_runner.py index cac740d..6e35060 100644 --- a/packages/relarena/tests/test_runner.py +++ b/packages/relarena/tests/test_runner.py @@ -16,10 +16,10 @@ from relbench.metrics import roc_auc from relarena import runner -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity -from relarena.core.results import TrialResult -from relarena.core.system import RelArenaSystem +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity +from relarena_core.results import TrialResult +from relarena_core.system import RelArenaSystem _MODEL = SimpleNamespace( name="stub", supported_task_types=frozenset({TaskType.BINARY_CLASSIFICATION}) diff --git a/packages/relarena/tests/userdb/test_query.py b/packages/relarena/tests/userdb/test_query.py index 5965bd1..e3cc8c7 100644 --- a/packages/relarena/tests/userdb/test_query.py +++ b/packages/relarena/tests/userdb/test_query.py @@ -11,10 +11,10 @@ import pandas as pd import pytest -from relarena.core.cache import CacheConfig -from relarena.core.identity import RunIdentity -from relarena.core.userdb.query import PredictiveQuery, PredictiveQuerySpec from relarena.userdb import relbench_v1_spec, relbench_v1_tasks +from relarena_core.cache import CacheConfig +from relarena_core.identity import RunIdentity +from relarena_core.userdb.query import PredictiveQuery, PredictiveQuerySpec _EXAMPLES = Path(__file__).resolve().parents[4] / "examples" _DB_YAML = "drivers:\n pkey: driverId\n" @@ -175,7 +175,7 @@ def test__precompute_cache__delegates_to_dfs_owner( query = _schema_only_query(data_version="v1") query._source = Mock() warm = Mock() - monkeypatch.setattr("relarena.core.featurization.warm_cache.warm_dfs_cache", warm) + monkeypatch.setattr("relarena_core.featurization.warm_cache.warm_dfs_cache", warm) assert query.precompute_cache(tmp_path) == tmp_path @@ -226,7 +226,7 @@ def test__predict__anchor_after_test_cutoff__warns_about_frozen_db( query._at_timestamp = pd.Timestamp("2020-02-01") query._entities = "all" predict_at = Mock(return_value=pd.DataFrame({"driverId": [], "y_pred": []})) - monkeypatch.setattr("relarena.core.userdb.query.predict_at", predict_at) + monkeypatch.setattr("relarena_core.userdb.query.predict_at", predict_at) with pytest.warns(UserWarning, match="feature database remains frozen"): query.predict() diff --git a/pyproject.toml b/pyproject.toml index da7d3b6..d74f14f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,11 +12,11 @@ dev = [ "kurversc==0.1.1", "lightgbm>=4.0,<4.7", "fastdfs>=1.1", - # Same woodwork/pkg_resources cap as the DFS extras above. + # Match the woodwork/pkg_resources cap in the core DFS extra. "setuptools<82", ] # The "cuda" and "cpu" groups switch between the cuda and CPU-only versions of torch. -# With no group, torch comes from "dependencies" above via pypi, which is the cuda +# With no group, torch comes from package dependencies via pypi, which is the cuda # build. The "cuda" group is a dummy, required for the "conflicts" option below. cuda = [] # The CPU-only build avoids downloading CUDA when not required. @@ -37,6 +37,7 @@ conflicts = [ [tool.uv.sources] relarena = { workspace = true } +relarena-core = { workspace = true } # bencheval lives in the autogluon/tabarena uv-workspace under packages/bencheval. # The resolved commit is pinned in uv.lock for reproducibility. bencheval = { git = "https://github.com/autogluon/tabarena.git", subdirectory = "packages/bencheval", rev = "9f3878b882439cb885003b5f978676961b059b6d" } @@ -50,7 +51,7 @@ url = "https://download.pytorch.org/whl/cpu" explicit = true [tool.ruff] -src = ["packages/relarena/src"] +src = ["packages/relarena/src", "packages/relarena-core/src"] required-version = "==0.15.13" force-exclude = true line-length = 88 @@ -93,8 +94,8 @@ ignore-decorators = ["typing.override", "typing_extensions.override"] ] [tool.uv.workspace] -members = ["packages/relarena"] +members = ["packages/relarena", "packages/relarena-core"] [tool.pytest.ini_options] -testpaths = ["packages/relarena/tests"] +testpaths = ["packages/relarena/tests", "packages/relarena-core/tests"] addopts = "--import-mode=importlib" diff --git a/uv.lock b/uv.lock index add472f..baf338b 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ conflicts = [[ ]] [options] -exclude-newer = "2026-09-06T14:27:48.950532Z" +exclude-newer = "2026-09-06T14:33:43.976773Z" exclude-newer-span = "P3D" [options.exclude-newer-package] @@ -28,6 +28,7 @@ kurversc = false [manifest] members = [ "relarena", + "relarena-core", "relarena-workspace", ] @@ -2392,7 +2393,7 @@ wheels = [ [[package]] name = "relarena" -version = "0.0.1" +version = "0.0.3" source = { editable = "packages/relarena" } dependencies = [ { name = "configspace" }, @@ -2400,6 +2401,7 @@ dependencies = [ { name = "numpy" }, { name = "pandas" }, { name = "pyyaml" }, + { name = "relarena-core" }, { name = "relbench" }, { name = "scikit-learn" }, { name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'group-18-relarena-workspace-cpu') or (extra == 'group-18-relarena-workspace-cpu' and extra == 'group-18-relarena-workspace-cuda')" }, @@ -2427,8 +2429,7 @@ plots = [ { name = "bencheval", extra = ["plot"] }, ] rdblearn = [ - { name = "fastdfs" }, - { name = "setuptools" }, + { name = "relarena-core", extra = ["dfs"] }, { name = "tabpfn" }, ] rdl = [ @@ -2457,8 +2458,7 @@ tabpfn-rel-api = [ { name = "tabpfn-client" }, ] tabpfn-rel-local = [ - { name = "fastdfs" }, - { name = "setuptools" }, + { name = "relarena-core", extra = ["dfs"] }, { name = "tabpfn" }, ] @@ -2469,7 +2469,6 @@ requires-dist = [ { name = "bencheval", extras = ["plot"], marker = "extra == 'plots'", git = "https://github.com/autogluon/tabarena.git?subdirectory=packages%2Fbencheval&rev=9f3878b882439cb885003b5f978676961b059b6d" }, { name = "configspace", specifier = ">=1.0" }, { name = "einops", marker = "extra == 'relgt'", specifier = ">=0.8" }, - { name = "fastdfs", marker = "extra == 'rdblearn'", specifier = ">=1.1" }, { name = "fastdfs", marker = "extra == 'tabpfn-rel-api'", specifier = ">=1.1" }, { name = "h5py", marker = "extra == 'relgt'", specifier = ">=3.0" }, { name = "jsonschema", specifier = ">=4.0" }, @@ -2483,11 +2482,12 @@ requires-dist = [ { name = "relarena", extras = ["rdl"], marker = "extra == 'graphsage'", editable = "packages/relarena" }, { name = "relarena", extras = ["rdl"], marker = "extra == 'relgnn'", editable = "packages/relarena" }, { name = "relarena", extras = ["rdl"], marker = "extra == 'relgt'", editable = "packages/relarena" }, + { name = "relarena-core", editable = "packages/relarena-core" }, + { name = "relarena-core", extras = ["dfs"], marker = "extra == 'rdblearn'", editable = "packages/relarena-core" }, { name = "relational-transformer", marker = "extra == 'rt'", specifier = "==1.8.0" }, { name = "relbench", specifier = "==2.1.2" }, { name = "scikit-learn", specifier = ">=1.3" }, { name = "sentence-transformers", marker = "extra == 'rdl'" }, - { name = "setuptools", marker = "extra == 'rdblearn'", specifier = "<82" }, { name = "setuptools", marker = "extra == 'tabpfn-rel-api'", specifier = "<82" }, { name = "tabpfn", marker = "extra == 'rdblearn'", specifier = ">=8" }, { name = "tabpfn-client", marker = "extra == 'tabpfn-rel-api'", specifier = ">=0.3.2" }, @@ -2496,6 +2496,46 @@ requires-dist = [ ] provides-extras = ["lightgbm", "kurversc", "rdblearn", "rdl", "graphsage", "relgnn", "relgt", "tabpfn-rel-local", "tabpfn-rel-api", "rt", "leaderboard", "plots"] +[[package]] +name = "relarena-core" +version = "0.0.1" +source = { editable = "packages/relarena-core" } +dependencies = [ + { name = "configspace" }, + { name = "duckdb" }, + { name = "jsonschema" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyyaml" }, + { name = "relbench" }, + { name = "scikit-learn" }, + { name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'group-18-relarena-workspace-cpu') or (extra == 'group-18-relarena-workspace-cpu' and extra == 'group-18-relarena-workspace-cuda')" }, + { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-18-relarena-workspace-cuda' or extra != 'group-18-relarena-workspace-cpu'" }, + { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'group-18-relarena-workspace-cpu') or (extra == 'group-18-relarena-workspace-cpu' and extra == 'group-18-relarena-workspace-cuda')" }, +] + +[package.optional-dependencies] +dfs = [ + { name = "fastdfs" }, + { name = "setuptools" }, +] + +[package.metadata] +requires-dist = [ + { name = "configspace", specifier = ">=1.0" }, + { name = "duckdb", specifier = ">=1.2.2" }, + { name = "fastdfs", marker = "extra == 'dfs'", specifier = ">=1.1" }, + { name = "jsonschema", specifier = ">=4.0" }, + { name = "numpy", specifier = ">=1.24" }, + { name = "pandas", specifier = ">=2.3.3,<3.0" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "relbench", specifier = "==2.1.2" }, + { name = "scikit-learn", specifier = ">=1.3" }, + { name = "setuptools", marker = "extra == 'dfs'", specifier = "<82" }, + { name = "torch" }, +] +provides-extras = ["dfs"] + [[package]] name = "relarena-workspace" version = "0.0.0" diff --git a/workflows/audit_licenses.py b/workflows/audit_licenses.py index 5e69d1b..0c1f8c1 100644 --- a/workflows/audit_licenses.py +++ b/workflows/audit_licenses.py @@ -160,6 +160,8 @@ #: Licenses for packages whose published metadata declares none, read by hand at the #: locked version from the artifact or the upstream repository. MANUAL_LICENSES: dict[str, str] = { + # packages/relarena-core/pyproject.toml declares Apache-2.0. + "relarena-core": "Apache-2.0", # autogluon/tabarena @ 221c38d: packages/bencheval/pyproject.toml declares # Apache-2.0, matching this repository's LICENSE (no per-package file). "bencheval": "Apache-2.0", diff --git a/workflows/smoke_feature_cache.py b/workflows/smoke_feature_cache.py index ce61b04..d672c50 100644 --- a/workflows/smoke_feature_cache.py +++ b/workflows/smoke_feature_cache.py @@ -17,12 +17,12 @@ import time from pathlib import Path -import relarena.core.featurization.dfs as dfs_mod -from relarena.core.cache import CacheConfig -from relarena.core.dataset import concat_tables -from relarena.core.featurization import DFS_MAX_DEPTH, build_dfs_features -from relarena.core.featurization.warm_cache import warm_dfs_cache +import relarena_core.featurization.dfs as dfs_mod from relarena.dataset import RelBenchDatasetTask +from relarena_core.cache import CacheConfig +from relarena_core.dataset import concat_tables +from relarena_core.featurization import DFS_MAX_DEPTH, build_dfs_features +from relarena_core.featurization.warm_cache import warm_dfs_cache DATASET, TASK = "rel-f1", "driver-dnf" diff --git a/workflows/verify_distributions.py b/workflows/verify_distributions.py index 4196508..7dc8553 100644 --- a/workflows/verify_distributions.py +++ b/workflows/verify_distributions.py @@ -2,27 +2,37 @@ from __future__ import annotations +import argparse import tarfile import zipfile from pathlib import Path def main() -> None: - """Check the wheel and sdist under ``dist/``.""" - wheel = next(Path("dist").glob("*.whl")) - sdist = next(Path("dist").glob("*.tar.gz")) - spec_root = Path("packages/relarena/src/relarena/userdb/relbench_v1") + """Check required package data in the selected distribution directory.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--dist-dir", type=Path, default=Path("dist")) + args = parser.parse_args() + wheel = next(args.dist_dir.glob("relarena-*.whl")) + sdist = next(args.dist_dir.glob("relarena-*.tar.gz")) + source_root = Path("packages/relarena/src") + spec_root = source_root / "relarena/userdb/relbench_v1" required = { "relarena/models/VENDORED-LICENSES", "relarena/checksums/relbench_v1_checksums.json", - "relarena/core/userdb/database.schema.json", - "relarena/core/userdb/task.schema.json", - *( - str(path.relative_to("packages/relarena/src")) - for path in spec_root.glob("*/*.yaml") - ), + *(str(path.relative_to(source_root)) for path in spec_root.glob("*/*.yaml")), } + core_required = { + f"relarena_core/userdb/{name}.schema.json" for name in ("database", "task") + } + core_wheel = next(args.dist_dir.glob("relarena_core-*.whl")) + core_sdist = next(args.dist_dir.glob("relarena_core-*.tar.gz")) + with zipfile.ZipFile(core_wheel) as archive: + _assert_present(core_wheel, archive.namelist(), core_required) + with tarfile.open(core_sdist) as archive: + _assert_present(core_sdist, archive.getnames(), core_required) + with zipfile.ZipFile(wheel) as archive: _assert_present(wheel, archive.namelist(), required) with tarfile.open(sdist) as archive: diff --git a/workflows/verify_relbench_v1.py b/workflows/verify_relbench_v1.py index fd64aff..8999098 100644 --- a/workflows/verify_relbench_v1.py +++ b/workflows/verify_relbench_v1.py @@ -19,14 +19,14 @@ import pandas as pd from relbench.tasks import get_task -from relarena.core.userdb.ingest import build_dataset -from relarena.core.userdb.task import UserEntityTask from relarena.dataset import drop_noncanonical_task_columns from relarena.userdb import ( materialize_relbench, relbench_v1_spec, relbench_v1_tasks, ) +from relarena_core.userdb.ingest import build_dataset +from relarena_core.userdb.task import UserEntityTask def _split_matches(want: pd.DataFrame, got: pd.DataFrame, keys: list[str]) -> bool: diff --git a/workflows/warm_feature_cache.py b/workflows/warm_feature_cache.py index abf4f0d..3af6308 100644 --- a/workflows/warm_feature_cache.py +++ b/workflows/warm_feature_cache.py @@ -29,10 +29,10 @@ import os import sys -from relarena.core.cache import resolve_cache_config -from relarena.core.featurization.warm_cache import warm_dfs_cache from relarena.dataset import RelBenchDatasetTask from relarena.tasks import RELBENCH_V1_DATASETS, list_entity_tasks +from relarena_core.cache import resolve_cache_config +from relarena_core.featurization.warm_cache import warm_dfs_cache def _parse_args(argv: list[str] | None) -> argparse.Namespace: From 2fed28c363c9850d248c1cb27fa947f5c3ac6667 Mon Sep 17 00:00:00 2001 From: Adrian Hayler Date: Fri, 11 Sep 2026 16:24:00 +0200 Subject: [PATCH 2/2] Scan entry points once, drop torch from core, set relarena 0.0.2 discover_models returns immediately after one complete pass, so per-lookup callers such as leaderboard.method_kind no longer rescan installed metadata. core imports torch only inside default_device, so it is no longer a dependency. The workflow is checked out at the default path. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 5 ----- packages/relarena-core/pyproject.toml | 1 - packages/relarena-core/src/relarena_core/discovery.py | 10 ++++++++-- packages/relarena-core/tests/test_discovery.py | 1 + packages/relarena/pyproject.toml | 2 +- packages/relarena/src/relarena/__init__.py | 2 +- uv.lock | 8 ++------ 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce957e9..cb70755 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,17 +16,12 @@ jobs: runs-on: ubuntu-latest env: UV_PYTHON: ${{ matrix.python-version }} - defaults: - run: - working-directory: relarena strategy: fail-fast: false matrix: python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - path: relarena - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} diff --git a/packages/relarena-core/pyproject.toml b/packages/relarena-core/pyproject.toml index bbf9fcb..2b5e036 100644 --- a/packages/relarena-core/pyproject.toml +++ b/packages/relarena-core/pyproject.toml @@ -20,7 +20,6 @@ dependencies = [ "pyyaml>=6.0", "jsonschema>=4.0", "duckdb>=1.2.2", - "torch", ] [project.optional-dependencies] diff --git a/packages/relarena-core/src/relarena_core/discovery.py b/packages/relarena-core/src/relarena_core/discovery.py index bdb5758..aa8bd33 100644 --- a/packages/relarena-core/src/relarena_core/discovery.py +++ b/packages/relarena-core/src/relarena_core/discovery.py @@ -6,6 +6,7 @@ from threading import RLock _loaded: set[tuple[str, str]] = set() +_complete = False _lock = RLock() @@ -13,10 +14,14 @@ def discover_models() -> None: """Import model modules declared in the relarena.models entry-point group. Module imports execute registration decorators against the shared registry. - Successful entries load once per process; failed imports remain retryable - and raise an error naming the plugin. Importing core does not run discovery. + After one pass without failures the call returns immediately, so callers can + invoke it per lookup. A failed import raises an error naming the plugin and + leaves discovery retryable. Importing core does not run discovery. """ + global _complete with _lock: + if _complete: + return for entry in sorted( entry_points(group="relarena.models"), key=lambda e: (e.name, e.value) ): @@ -31,6 +36,7 @@ def discover_models() -> None: f"({entry.value}). Check its installation and dependencies." ) from exc _loaded.add(key) + _complete = True __all__ = ["discover_models"] diff --git a/packages/relarena-core/tests/test_discovery.py b/packages/relarena-core/tests/test_discovery.py index 0c22bd3..4d5edf4 100644 --- a/packages/relarena-core/tests/test_discovery.py +++ b/packages/relarena-core/tests/test_discovery.py @@ -21,6 +21,7 @@ def isolated(monkeypatch: pytest.MonkeyPatch) -> MethodRegistry: registry = MethodRegistry() monkeypatch.setattr(registry_module, "registry", registry) monkeypatch.setattr(discovery, "_loaded", set()) + monkeypatch.setattr(discovery, "_complete", False) return registry diff --git a/packages/relarena/pyproject.toml b/packages/relarena/pyproject.toml index f0a44eb..c8ea8ff 100644 --- a/packages/relarena/pyproject.toml +++ b/packages/relarena/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "relarena" -version = "0.0.3" +version = "0.0.2" authors = [{ name = "Prior Labs" }] license = "Apache-2.0" license-files = ["LICENSE", "NOTICE", "src/relarena/models/VENDORED-LICENSES"] diff --git a/packages/relarena/src/relarena/__init__.py b/packages/relarena/src/relarena/__init__.py index f6ced0f..bf07844 100644 --- a/packages/relarena/src/relarena/__init__.py +++ b/packages/relarena/src/relarena/__init__.py @@ -72,4 +72,4 @@ "database_checksum", "split_checksums", ] -__version__ = "0.0.3" +__version__ = "0.0.2" diff --git a/uv.lock b/uv.lock index baf338b..c2cbb17 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ conflicts = [[ ]] [options] -exclude-newer = "2026-09-06T14:33:43.976773Z" +exclude-newer = "2026-09-08T14:22:38.664869Z" exclude-newer-span = "P3D" [options.exclude-newer-package] @@ -2393,7 +2393,7 @@ wheels = [ [[package]] name = "relarena" -version = "0.0.3" +version = "0.0.2" source = { editable = "packages/relarena" } dependencies = [ { name = "configspace" }, @@ -2509,9 +2509,6 @@ dependencies = [ { name = "pyyaml" }, { name = "relbench" }, { name = "scikit-learn" }, - { name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'darwin' and extra == 'group-18-relarena-workspace-cpu') or (extra == 'group-18-relarena-workspace-cpu' and extra == 'group-18-relarena-workspace-cuda')" }, - { name = "torch", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-18-relarena-workspace-cuda' or extra != 'group-18-relarena-workspace-cpu'" }, - { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'group-18-relarena-workspace-cpu') or (extra == 'group-18-relarena-workspace-cpu' and extra == 'group-18-relarena-workspace-cuda')" }, ] [package.optional-dependencies] @@ -2532,7 +2529,6 @@ requires-dist = [ { name = "relbench", specifier = "==2.1.2" }, { name = "scikit-learn", specifier = ">=1.3" }, { name = "setuptools", marker = "extra == 'dfs'", specifier = "<82" }, - { name = "torch" }, ] provides-extras = ["dfs"]