comparator-recommender 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comparator_recommender-0.1.1/.github/release-drafter.yml +31 -0
- comparator_recommender-0.1.1/.github/workflows/ci.yml +10 -0
- comparator_recommender-0.1.1/.github/workflows/compatibility.yml +65 -0
- comparator_recommender-0.1.1/.github/workflows/merge.yml +13 -0
- comparator_recommender-0.1.1/.github/workflows/publish.yml +21 -0
- comparator_recommender-0.1.1/.gitignore +9 -0
- comparator_recommender-0.1.1/PKG-INFO +94 -0
- comparator_recommender-0.1.1/README.md +79 -0
- comparator_recommender-0.1.1/pyproject.toml +52 -0
- comparator_recommender-0.1.1/src/comparator_recommender/__init__.py +10 -0
- comparator_recommender-0.1.1/src/comparator_recommender/adapters.py +57 -0
- comparator_recommender-0.1.1/src/comparator_recommender/backends/__init__.py +6 -0
- comparator_recommender-0.1.1/src/comparator_recommender/backends/base.py +54 -0
- comparator_recommender-0.1.1/src/comparator_recommender/backends/postgresql.py +620 -0
- comparator_recommender-0.1.1/src/comparator_recommender/config.py +29 -0
- comparator_recommender-0.1.1/src/comparator_recommender/models.py +98 -0
- comparator_recommender-0.1.1/src/comparator_recommender/plugin.py +197 -0
- comparator_recommender-0.1.1/src/comparator_recommender/services.py +74 -0
- comparator_recommender-0.1.1/src/comparator_recommender/setup_cli.py +77 -0
- comparator_recommender-0.1.1/src/comparator_recommender/tools.py +47 -0
- comparator_recommender-0.1.1/tests/test_adapters.py +22 -0
- comparator_recommender-0.1.1/tests/test_configuration_mwe.py +194 -0
- comparator_recommender-0.1.1/tests/test_sqlalchemy_backend.py +310 -0
- comparator_recommender-0.1.1/uv.lock +4257 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name-template: 'v$RESOLVED_VERSION'
|
|
2
|
+
tag-template: 'v$RESOLVED_VERSION'
|
|
3
|
+
commitish: main
|
|
4
|
+
|
|
5
|
+
categories:
|
|
6
|
+
- title: Breaking Changes
|
|
7
|
+
labels: ['breaking']
|
|
8
|
+
- title: Features
|
|
9
|
+
labels: ['feature']
|
|
10
|
+
- title: Fixes
|
|
11
|
+
labels: ['fix']
|
|
12
|
+
- title: Dependencies
|
|
13
|
+
labels: ['dependencies']
|
|
14
|
+
|
|
15
|
+
template: |
|
|
16
|
+
$CHANGES
|
|
17
|
+
|
|
18
|
+
change-template: '- **$TITLE** (#$NUMBER) @$AUTHOR'
|
|
19
|
+
|
|
20
|
+
version-resolver:
|
|
21
|
+
major:
|
|
22
|
+
labels: ['breaking']
|
|
23
|
+
minor:
|
|
24
|
+
labels: ['feature']
|
|
25
|
+
patch:
|
|
26
|
+
labels: ['fix', 'dependencies']
|
|
27
|
+
default: patch
|
|
28
|
+
|
|
29
|
+
exclude-labels: ['chore']
|
|
30
|
+
|
|
31
|
+
autolabeler: []
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [main]
|
|
5
|
+
types: [opened, synchronize, reopened, labeled, unlabeled]
|
|
6
|
+
jobs:
|
|
7
|
+
label-gate:
|
|
8
|
+
uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/label-gate.yml@main
|
|
9
|
+
build-test:
|
|
10
|
+
uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/build-test.yml@main
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Compatibility
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- .github/workflows/compatibility.yml
|
|
8
|
+
- pyproject.toml
|
|
9
|
+
- uv.lock
|
|
10
|
+
- src/**
|
|
11
|
+
- tests/**
|
|
12
|
+
push:
|
|
13
|
+
tags: ['v*']
|
|
14
|
+
repository_dispatch:
|
|
15
|
+
types: [groundworkers-compatibility]
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
inputs:
|
|
18
|
+
groundworkers_version:
|
|
19
|
+
description: Released Groundworkers version to test
|
|
20
|
+
required: true
|
|
21
|
+
default: 0.7.0
|
|
22
|
+
python_versions:
|
|
23
|
+
description: Comma-separated Python versions to test
|
|
24
|
+
required: true
|
|
25
|
+
default: 3.12,3.13
|
|
26
|
+
schedule:
|
|
27
|
+
- cron: "17 3 * * 1"
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
prepare:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
outputs:
|
|
36
|
+
include: ${{ steps.matrix.outputs.include }}
|
|
37
|
+
steps:
|
|
38
|
+
- id: matrix
|
|
39
|
+
env:
|
|
40
|
+
REQUESTED_GROUNDWORKERS: ${{ github.event.client_payload.groundworkers_version || inputs.groundworkers_version || '0.7.0' }}
|
|
41
|
+
REQUESTED_PYTHON_JSON: ${{ toJSON(github.event.client_payload.python_versions) }}
|
|
42
|
+
INPUT_PYTHON_VERSIONS: ${{ inputs.python_versions || '3.12,3.13' }}
|
|
43
|
+
run: |
|
|
44
|
+
python -c 'import json, os; raw=os.environ["REQUESTED_PYTHON_JSON"]; versions=json.loads(raw) if raw not in ("", "null") else [v.strip() for v in os.environ["INPUT_PYTHON_VERSIONS"].split(",") if v.strip()]; gw=os.environ["REQUESTED_GROUNDWORKERS"]; print("include=" + json.dumps({"include": [{"python": version, "groundworkers": gw} for version in versions]}, separators=(",", ":")))' >> "$GITHUB_OUTPUT"
|
|
45
|
+
|
|
46
|
+
compatibility:
|
|
47
|
+
needs: prepare
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
strategy:
|
|
50
|
+
fail-fast: false
|
|
51
|
+
matrix: ${{ fromJSON(needs.prepare.outputs.include) }}
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: ${{ matrix.python }}
|
|
57
|
+
- uses: astral-sh/setup-uv@v6
|
|
58
|
+
with:
|
|
59
|
+
enable-cache: true
|
|
60
|
+
- run: uv build --wheel --out-dir compatibility-dist
|
|
61
|
+
- run: uv venv --python "${{ matrix.python }}" .compatibility-venv
|
|
62
|
+
- run: uv pip install --python .compatibility-venv/bin/python "groundworkers==${{ matrix.groundworkers }}"
|
|
63
|
+
- run: uv pip install --python .compatibility-venv/bin/python compatibility-dist/*.whl "pytest>=9,<10"
|
|
64
|
+
- run: .compatibility-venv/bin/python -m pytest -q
|
|
65
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
name: Release Update
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types: [closed]
|
|
5
|
+
branches: [main]
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
pull-requests: read
|
|
9
|
+
jobs:
|
|
10
|
+
draft:
|
|
11
|
+
if: github.event.pull_request.merged == true
|
|
12
|
+
uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/release-drafter.yml@main
|
|
13
|
+
secrets: inherit
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ['v*']
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
uses: AustralianCancerDataNetwork/cava-devops/.github/workflows/publish.yml@main
|
|
8
|
+
publish:
|
|
9
|
+
# Inline: PyPI OIDC checks job_workflow_ref, which must point to this file.
|
|
10
|
+
# Moving pypa/gh-action-pypi-publish into cava-devops would break the trusted publisher.
|
|
11
|
+
needs: build
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/p/comparator-recommender
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/download-artifact@v4
|
|
20
|
+
with: { name: dist, path: dist/ }
|
|
21
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: comparator-recommender
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Groundworkers plugin for recommending comparator cohorts.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: groundworkers[tui]<1,>=0.7
|
|
7
|
+
Requires-Dist: oa-configurator<3,>=2
|
|
8
|
+
Requires-Dist: omop-emb<4,>=3
|
|
9
|
+
Requires-Dist: omop-llm<2,>=1
|
|
10
|
+
Requires-Dist: pandas>=3
|
|
11
|
+
Requires-Dist: pgvector<1,>=0.5
|
|
12
|
+
Requires-Dist: pydantic<3,>=2
|
|
13
|
+
Requires-Dist: sqlalchemy<3,>=2
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Comparator Recommender
|
|
17
|
+
|
|
18
|
+
This package is an installable Groundworkers plugin for finding target cohorts
|
|
19
|
+
and recommending pairwise comparators from a precomputed comparator catalogue.
|
|
20
|
+
|
|
21
|
+
It contributes two entry points:
|
|
22
|
+
|
|
23
|
+
- `omop.config` exposes `ComparatorRecommenderConfig` to oa-configurator.
|
|
24
|
+
- `groundworkers.plugins` exposes the plugin singleton to Groundworkers.
|
|
25
|
+
|
|
26
|
+
The configuration schema selects a generic comparator database, optionally selects an embedding model for target lookup, and sets the default minimum database support threshold. Groundworkers builds the setup-console workflow directly from this schema without plugin-specific TUI code.
|
|
27
|
+
|
|
28
|
+
The setup console also exposes the plugin's registered preparation steps:
|
|
29
|
+
|
|
30
|
+
- `Load comparator source` asks for the cohort and pairwise-similarity CSVs and loads them into the catalogue.
|
|
31
|
+
- `Create comparator embeddings` generates the stored vectors after the source is loaded.
|
|
32
|
+
|
|
33
|
+
The CSV paths are run arguments, not persistent plugin configuration. Both steps use Groundworkers' durable maintenance-run surface, so progress and logs appear with the core graph and embedding preparation runs.
|
|
34
|
+
|
|
35
|
+
Run the MWE against the published Groundworkers 0.7 stack:
|
|
36
|
+
|
|
37
|
+
```console
|
|
38
|
+
uv sync --group dev
|
|
39
|
+
uv run pytest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The acceptance test selects an existing CDM database, leaves the optional
|
|
43
|
+
embedding resources unconfigured, reviews the generated change, applies it, and
|
|
44
|
+
verifies the resulting `[tools.comparator_recommender]` section.
|
|
45
|
+
|
|
46
|
+
## Upgrading to the Groundworkers 0.7 stack
|
|
47
|
+
|
|
48
|
+
This plugin requires `groundworkers[tui]>=0.7,<1`, `oa-configurator>=2,<3`
|
|
49
|
+
and `omop-emb>=3,<4`. For CDM database entries, replace the former
|
|
50
|
+
`schema_name` setting with `cdm_schema`; generic databases such as
|
|
51
|
+
`comparator_db` still use `schema_name`. Remove schema settings from SQLite
|
|
52
|
+
database entries. See the [Groundworkers upgrade guide](https://github.com/AustralianCancerDataNetwork/groundworkers/blob/main/docs/usage/upgrading-from-0.6.md)
|
|
53
|
+
for the shared configuration changes.
|
|
54
|
+
|
|
55
|
+
**The embedding-store migration script is not available yet.** It is tracked
|
|
56
|
+
in [oa-configurator issue #40](https://github.com/AustralianCancerDataNetwork/oa-configurator/issues/40).
|
|
57
|
+
Existing shared stores created before omop-emb 3.0 report `LEGACY_REGISTRY`.
|
|
58
|
+
The [omop-emb legacy-registry guard and migration-command placeholder](https://github.com/AustralianCancerDataNetwork/omop-emb/blob/main/src/omop_emb/model_registry/model_registry_orm.py)
|
|
59
|
+
remain the source of the planned migration command; follow the issue for its
|
|
60
|
+
availability before attempting migration. This plugin does not migrate shared
|
|
61
|
+
embedding stores.
|
|
62
|
+
|
|
63
|
+
Comparator vectors live in `cs_cohort.embedding_vector` in the comparator
|
|
64
|
+
database. The plugin uses omop-emb to generate vectors directly from the selected
|
|
65
|
+
model, without opening the shared embedding-store registry. Upgrading the Python
|
|
66
|
+
packages does not migrate or regenerate existing comparator vectors. Keep the
|
|
67
|
+
same embedding model for creation and lookup; if changing models, explicitly
|
|
68
|
+
rerun `Create comparator embeddings` after loading the source catalogue.
|
|
69
|
+
|
|
70
|
+
## Architecture
|
|
71
|
+
|
|
72
|
+
The package separates four responsibilities:
|
|
73
|
+
|
|
74
|
+
- `config.py` declares operator-controlled settings and references to shared OA
|
|
75
|
+
resources.
|
|
76
|
+
- `adapters.py` owns the resolved database connection and backend.
|
|
77
|
+
- `models.py` reflects the two non-OMOP catalogue tables into SQLAlchemy mappings.
|
|
78
|
+
- `backends/postgresql.py` uses SQLAlchemy `select`, pgvector expressions,
|
|
79
|
+
mapped row updates, and `Index` objects, with one isolated column migration
|
|
80
|
+
for older catalogue tables. Its backend initialization follows the
|
|
81
|
+
`omop-emb` lifecycle and ensures the pgvector extension before setup runs.
|
|
82
|
+
- `services.py` owns input policy and recommendation defaults. It receives
|
|
83
|
+
ordinary Python values from adapters and contains no MCP code.
|
|
84
|
+
- `tools.py` exposes service operations to agents. Tools should be thin wrappers,
|
|
85
|
+
not a second implementation of the analysis.
|
|
86
|
+
|
|
87
|
+
`plugin.py` is the composition root: Groundworkers calls it once during startup
|
|
88
|
+
to connect configuration to adapters and services, then asks it to register the
|
|
89
|
+
tools. Business logic should not be implemented inside `build()`.
|
|
90
|
+
|
|
91
|
+
The comparator catalogue is not an OMOP CDM table, so it should not be added to
|
|
92
|
+
`omop-alchemy`'s model registry. The local reflected mapping follows the same
|
|
93
|
+
SQLAlchemy model/query conventions while leaving the shared OMOP package focused
|
|
94
|
+
on CDM semantics.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Comparator Recommender
|
|
2
|
+
|
|
3
|
+
This package is an installable Groundworkers plugin for finding target cohorts
|
|
4
|
+
and recommending pairwise comparators from a precomputed comparator catalogue.
|
|
5
|
+
|
|
6
|
+
It contributes two entry points:
|
|
7
|
+
|
|
8
|
+
- `omop.config` exposes `ComparatorRecommenderConfig` to oa-configurator.
|
|
9
|
+
- `groundworkers.plugins` exposes the plugin singleton to Groundworkers.
|
|
10
|
+
|
|
11
|
+
The configuration schema selects a generic comparator database, optionally selects an embedding model for target lookup, and sets the default minimum database support threshold. Groundworkers builds the setup-console workflow directly from this schema without plugin-specific TUI code.
|
|
12
|
+
|
|
13
|
+
The setup console also exposes the plugin's registered preparation steps:
|
|
14
|
+
|
|
15
|
+
- `Load comparator source` asks for the cohort and pairwise-similarity CSVs and loads them into the catalogue.
|
|
16
|
+
- `Create comparator embeddings` generates the stored vectors after the source is loaded.
|
|
17
|
+
|
|
18
|
+
The CSV paths are run arguments, not persistent plugin configuration. Both steps use Groundworkers' durable maintenance-run surface, so progress and logs appear with the core graph and embedding preparation runs.
|
|
19
|
+
|
|
20
|
+
Run the MWE against the published Groundworkers 0.7 stack:
|
|
21
|
+
|
|
22
|
+
```console
|
|
23
|
+
uv sync --group dev
|
|
24
|
+
uv run pytest
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The acceptance test selects an existing CDM database, leaves the optional
|
|
28
|
+
embedding resources unconfigured, reviews the generated change, applies it, and
|
|
29
|
+
verifies the resulting `[tools.comparator_recommender]` section.
|
|
30
|
+
|
|
31
|
+
## Upgrading to the Groundworkers 0.7 stack
|
|
32
|
+
|
|
33
|
+
This plugin requires `groundworkers[tui]>=0.7,<1`, `oa-configurator>=2,<3`
|
|
34
|
+
and `omop-emb>=3,<4`. For CDM database entries, replace the former
|
|
35
|
+
`schema_name` setting with `cdm_schema`; generic databases such as
|
|
36
|
+
`comparator_db` still use `schema_name`. Remove schema settings from SQLite
|
|
37
|
+
database entries. See the [Groundworkers upgrade guide](https://github.com/AustralianCancerDataNetwork/groundworkers/blob/main/docs/usage/upgrading-from-0.6.md)
|
|
38
|
+
for the shared configuration changes.
|
|
39
|
+
|
|
40
|
+
**The embedding-store migration script is not available yet.** It is tracked
|
|
41
|
+
in [oa-configurator issue #40](https://github.com/AustralianCancerDataNetwork/oa-configurator/issues/40).
|
|
42
|
+
Existing shared stores created before omop-emb 3.0 report `LEGACY_REGISTRY`.
|
|
43
|
+
The [omop-emb legacy-registry guard and migration-command placeholder](https://github.com/AustralianCancerDataNetwork/omop-emb/blob/main/src/omop_emb/model_registry/model_registry_orm.py)
|
|
44
|
+
remain the source of the planned migration command; follow the issue for its
|
|
45
|
+
availability before attempting migration. This plugin does not migrate shared
|
|
46
|
+
embedding stores.
|
|
47
|
+
|
|
48
|
+
Comparator vectors live in `cs_cohort.embedding_vector` in the comparator
|
|
49
|
+
database. The plugin uses omop-emb to generate vectors directly from the selected
|
|
50
|
+
model, without opening the shared embedding-store registry. Upgrading the Python
|
|
51
|
+
packages does not migrate or regenerate existing comparator vectors. Keep the
|
|
52
|
+
same embedding model for creation and lookup; if changing models, explicitly
|
|
53
|
+
rerun `Create comparator embeddings` after loading the source catalogue.
|
|
54
|
+
|
|
55
|
+
## Architecture
|
|
56
|
+
|
|
57
|
+
The package separates four responsibilities:
|
|
58
|
+
|
|
59
|
+
- `config.py` declares operator-controlled settings and references to shared OA
|
|
60
|
+
resources.
|
|
61
|
+
- `adapters.py` owns the resolved database connection and backend.
|
|
62
|
+
- `models.py` reflects the two non-OMOP catalogue tables into SQLAlchemy mappings.
|
|
63
|
+
- `backends/postgresql.py` uses SQLAlchemy `select`, pgvector expressions,
|
|
64
|
+
mapped row updates, and `Index` objects, with one isolated column migration
|
|
65
|
+
for older catalogue tables. Its backend initialization follows the
|
|
66
|
+
`omop-emb` lifecycle and ensures the pgvector extension before setup runs.
|
|
67
|
+
- `services.py` owns input policy and recommendation defaults. It receives
|
|
68
|
+
ordinary Python values from adapters and contains no MCP code.
|
|
69
|
+
- `tools.py` exposes service operations to agents. Tools should be thin wrappers,
|
|
70
|
+
not a second implementation of the analysis.
|
|
71
|
+
|
|
72
|
+
`plugin.py` is the composition root: Groundworkers calls it once during startup
|
|
73
|
+
to connect configuration to adapters and services, then asks it to register the
|
|
74
|
+
tools. Business logic should not be implemented inside `build()`.
|
|
75
|
+
|
|
76
|
+
The comparator catalogue is not an OMOP CDM table, so it should not be added to
|
|
77
|
+
`omop-alchemy`'s model registry. The local reflected mapping follows the same
|
|
78
|
+
SQLAlchemy model/query conventions while leaving the shared OMOP package focused
|
|
79
|
+
on CDM semantics.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "comparator-recommender"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Groundworkers plugin for recommending comparator cohorts."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"groundworkers[tui]>=0.7,<1",
|
|
9
|
+
"oa-configurator>=2,<3",
|
|
10
|
+
"omop-emb>=3,<4",
|
|
11
|
+
"omop-llm>=1,<2",
|
|
12
|
+
"pandas>=3",
|
|
13
|
+
"pgvector>=0.5,<1",
|
|
14
|
+
"pydantic>=2,<3",
|
|
15
|
+
"SQLAlchemy>=2,<3",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.entry-points."omop.config"]
|
|
19
|
+
comparator_recommender = "comparator_recommender.config:ComparatorRecommenderConfig"
|
|
20
|
+
|
|
21
|
+
[project.entry-points."groundworkers.plugins"]
|
|
22
|
+
comparator_recommender = "comparator_recommender.plugin:plugin"
|
|
23
|
+
|
|
24
|
+
[dependency-groups]
|
|
25
|
+
dev = [
|
|
26
|
+
"groundskeeping>=0.9.1,<1",
|
|
27
|
+
"pytest>=9,<10",
|
|
28
|
+
"ruff>=0.4",
|
|
29
|
+
"ty>=0.0.59",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["hatchling"]
|
|
34
|
+
build-backend = "hatchling.build"
|
|
35
|
+
|
|
36
|
+
[tool.uv]
|
|
37
|
+
constraint-dependencies = ["setuptools>=83.0.0"]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/comparator_recommender"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
testpaths = ["tests"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
src = ["src", "tests"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = ["E4", "E7", "E9", "F", "I", "UP", "RUF"]
|
|
50
|
+
|
|
51
|
+
[tool.ty.src]
|
|
52
|
+
include = ["src"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Comparator Recommender plugin configuration MWE."""
|
|
2
|
+
|
|
3
|
+
from comparator_recommender.config import ComparatorRecommenderConfig
|
|
4
|
+
from comparator_recommender.plugin import ComparatorRecommenderPlugin, plugin
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"ComparatorRecommenderConfig",
|
|
8
|
+
"ComparatorRecommenderPlugin",
|
|
9
|
+
"plugin",
|
|
10
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Connections to the comparator catalogue and its embedding model."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from groundworkers.plugins import PluginReadinessResult
|
|
7
|
+
from oa_configurator import ResolvedDatabase
|
|
8
|
+
from omop_llm import ModelBackend
|
|
9
|
+
|
|
10
|
+
from comparator_recommender.backends import (
|
|
11
|
+
ComparatorSelectorBackend,
|
|
12
|
+
PostgresComparatorSelectorBackend,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class ComparatorDataAdapter:
|
|
18
|
+
"""Own the database-backed comparator operations."""
|
|
19
|
+
|
|
20
|
+
database: ResolvedDatabase
|
|
21
|
+
backend: ComparatorSelectorBackend
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def build(
|
|
25
|
+
cls,
|
|
26
|
+
database: ResolvedDatabase,
|
|
27
|
+
model_backend_factory: Callable[[], ModelBackend] | None = None,
|
|
28
|
+
model_source: str | None = None,
|
|
29
|
+
) -> "ComparatorDataAdapter":
|
|
30
|
+
return cls(
|
|
31
|
+
database=database,
|
|
32
|
+
backend=PostgresComparatorSelectorBackend(
|
|
33
|
+
engine=database.create_engine(register_claims=False),
|
|
34
|
+
schema=database.schema_name or "public",
|
|
35
|
+
model_backend_factory=model_backend_factory,
|
|
36
|
+
model_source=model_source,
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def find_target(self, name: str, top_n: int) -> list[tuple[int, str]]:
|
|
41
|
+
return self.backend.find_target(name, top_n)
|
|
42
|
+
|
|
43
|
+
def recommend_comparators(
|
|
44
|
+
self, target_id: int, top_n: int, min_databases: int
|
|
45
|
+
) -> list[tuple[float, str]]:
|
|
46
|
+
return self.backend.recommend_comparators(target_id, top_n, min_databases)
|
|
47
|
+
|
|
48
|
+
def insert_data(self, cohort_table_path: str, similarity_table_path: str) -> None:
|
|
49
|
+
self.backend.insert_data(cohort_table_path, similarity_table_path)
|
|
50
|
+
|
|
51
|
+
def create_embedding_vectors(self) -> None:
|
|
52
|
+
self.backend.create_embedding_vectors()
|
|
53
|
+
|
|
54
|
+
def verify_readiness(self) -> PluginReadinessResult:
|
|
55
|
+
"""Return the backend's read-only readiness report."""
|
|
56
|
+
|
|
57
|
+
return self.backend.verify_readiness()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Backend implementations for comparator catalogue storage."""
|
|
2
|
+
|
|
3
|
+
from comparator_recommender.backends.base import ComparatorSelectorBackend
|
|
4
|
+
from comparator_recommender.backends.postgresql import PostgresComparatorSelectorBackend
|
|
5
|
+
|
|
6
|
+
__all__ = ["ComparatorSelectorBackend", "PostgresComparatorSelectorBackend"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Storage-independent comparator backend contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
|
|
8
|
+
from groundworkers.plugins import PluginReadinessResult
|
|
9
|
+
from omop_llm import ModelBackend
|
|
10
|
+
from sqlalchemy import Engine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComparatorSelectorBackend(ABC):
|
|
14
|
+
"""Operations required by the comparator recommender service."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
engine: Engine,
|
|
19
|
+
schema: str,
|
|
20
|
+
model_backend_factory: Callable[[], ModelBackend] | None = None,
|
|
21
|
+
model_source: str | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
if not schema.strip():
|
|
24
|
+
raise ValueError("schema must not be empty")
|
|
25
|
+
self.engine = engine
|
|
26
|
+
self.schema = schema
|
|
27
|
+
self.model_backend_factory = model_backend_factory
|
|
28
|
+
self.model_source = model_source
|
|
29
|
+
|
|
30
|
+
def pre_initialise_store(self) -> None:
|
|
31
|
+
"""Prepare mutating setup prerequisites; the default is a no-op.
|
|
32
|
+
|
|
33
|
+
Backend construction and readiness checks are deliberately read-only.
|
|
34
|
+
Mutating setup operations call this hook explicitly when needed.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def find_target(self, name: str, top_n: int) -> list[tuple[int, str]]: ...
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def recommend_comparators(
|
|
42
|
+
self, target_id: int, top_n: int, min_databases: int
|
|
43
|
+
) -> list[tuple[float, str]]: ...
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def insert_data(
|
|
47
|
+
self, cohort_table_path: str, similarity_table_path: str
|
|
48
|
+
) -> None: ...
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def create_embedding_vectors(self) -> None: ...
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def verify_readiness(self) -> PluginReadinessResult: ...
|