meds-algorithm 0.4.0a4__tar.gz → 0.4.0a5__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.
Files changed (74) hide show
  1. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/.github/workflows/ci.yml +4 -1
  2. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/ARCHITECTURE.md +327 -67
  3. meds_algorithm-0.4.0a4/README.md → meds_algorithm-0.4.0a5/PKG-INFO +53 -7
  4. meds_algorithm-0.4.0a4/PKG-INFO → meds_algorithm-0.4.0a5/README.md +24 -31
  5. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/RELEASING.md +9 -9
  6. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/pyproject.toml +8 -2
  7. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/CONTRACT.md +13 -9
  8. meds_algorithm-0.4.0a5/src/meds_algorithm/ORCHESTRATION.md +163 -0
  9. meds_algorithm-0.4.0a5/src/meds_algorithm/TESTING.md +262 -0
  10. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/__init__.py +0 -4
  11. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/algorithms.py +1 -4
  12. meds_algorithm-0.4.0a5/src/meds_algorithm/conformance/cases.py +55 -0
  13. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/end_to_end.py +17 -13
  14. meds_algorithm-0.4.0a5/src/meds_algorithm/conformance/suite.py +153 -0
  15. meds_algorithm-0.4.0a5/src/meds_algorithm/contracts/commands.py +125 -0
  16. meds_algorithm-0.4.0a5/src/meds_algorithm/orchestration/__init__.py +1 -0
  17. meds_algorithm-0.4.0a5/src/meds_algorithm/orchestration/cli.py +42 -0
  18. meds_algorithm-0.4.0a5/src/meds_algorithm/orchestration/config.py +58 -0
  19. meds_algorithm-0.4.0a5/src/meds_algorithm/orchestration/execution.py +128 -0
  20. meds_algorithm-0.4.0a5/src/meds_algorithm/orchestration/planning.py +198 -0
  21. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/requests.py +10 -3
  22. meds_algorithm-0.4.0a5/src/meds_algorithm/runtime/process.py +57 -0
  23. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/spec.py +10 -0
  24. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_algorithms.py +62 -14
  25. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_conformance.py +64 -0
  26. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_core.py +2 -1
  27. meds_algorithm-0.4.0a5/tests/test_orchestration.py +324 -0
  28. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_process.py +8 -2
  29. meds_algorithm-0.4.0a5/tests/test_suite.py +145 -0
  30. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/wheel_smoke.py +8 -1
  31. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/uv.lock +34 -2
  32. meds_algorithm-0.4.0a4/src/meds_algorithm/contracts/commands.py +0 -182
  33. meds_algorithm-0.4.0a4/src/meds_algorithm/runtime/process.py +0 -50
  34. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/.github/workflows/publish.yml +0 -0
  35. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/.gitignore +0 -0
  36. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/LICENSE +0 -0
  37. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/__init__.py +0 -0
  38. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/inputs.py +0 -0
  39. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/manifest.py +0 -0
  40. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/publication.py +0 -0
  41. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/references.py +0 -0
  42. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/artifacts/results.py +0 -0
  43. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/configuration.py +0 -0
  44. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/__init__.py +0 -0
  45. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/assertions.py +0 -0
  46. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/meds_dev.py +0 -0
  47. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/reports.py +0 -0
  48. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/signal.py +0 -0
  49. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/conformance/types.py +0 -0
  50. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/contracts/__init__.py +0 -0
  51. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/contracts/enums.py +0 -0
  52. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/data/__init__.py +0 -0
  53. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/data/index.py +0 -0
  54. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/data/labels.py +0 -0
  55. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/data/predicates.py +0 -0
  56. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/data/validation.py +0 -0
  57. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/evaluation/__init__.py +0 -0
  58. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/evaluation/predictions.py +0 -0
  59. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/integrations/__init__.py +0 -0
  60. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/integrations/meds_dev.py +0 -0
  61. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/runner.py +0 -0
  62. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/runtime/__init__.py +0 -0
  63. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/runtime/provenance.py +0 -0
  64. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/runtime/resources.py +0 -0
  65. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/serialization.py +0 -0
  66. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/tasks/__init__.py +0 -0
  67. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/tasks/spec.py +0 -0
  68. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/src/meds_algorithm/version.py +0 -0
  69. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_artifacts.py +0 -0
  70. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_configuration.py +0 -0
  71. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_evaluation.py +0 -0
  72. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_integrations.py +0 -0
  73. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_predicates.py +0 -0
  74. {meds_algorithm-0.4.0a4 → meds_algorithm-0.4.0a5}/tests/test_reports.py +0 -0
@@ -20,6 +20,8 @@ jobs:
20
20
  - run: uv run ruff check .
21
21
  - run: uv run ruff format --check .
22
22
  - run: uv run pytest -q
23
+ - name: Test at the declared polars floor (algorithms may pin old polars)
24
+ run: uv run --with 'polars==1.0.0' pytest -q
23
25
  - run: uv build
24
26
  - name: Validate distribution metadata and PyPI README rendering
25
27
  run: uvx --from 'twine>=6' twine check --strict dist/*
@@ -31,9 +33,10 @@ jobs:
31
33
  - name: Run conformance tests against the installed wheel outside the checkout
32
34
  run: |
33
35
  wheel=$(realpath dist/*.whl)
34
- uv pip install --python /tmp/core-wheel/bin/python "${wheel}[conformance]"
36
+ uv pip install --python /tmp/core-wheel/bin/python "${wheel}[conformance,orchestration]"
35
37
  uv pip check --python /tmp/core-wheel/bin/python
36
38
  cp -r tests /tmp/core-wheel-tests
39
+ cp pyproject.toml /tmp/core-wheel-tests/
37
40
  cd /tmp
38
41
  /tmp/core-wheel/bin/python -I -m pytest -q /tmp/core-wheel-tests
39
42
  windows-wheel:
@@ -66,15 +66,27 @@ classDiagram
66
66
  Each profile class declares its `profile` and ordered `steps`; method annotations identify request
67
67
  types. Request fields define CLI arguments, while each request declares its output argument and
68
68
  result artifact class. There is no independent step-definition registry. `ALGORITHM_PROFILES` and
69
- `STEP_SPECS` are read-only, derived compatibility views for existing consumers.
69
+ `STEP_SPECS` are read-only views derived from these classes. `StepSpec.from_request_type` constructs
70
+ a specification for manifest validation and command inspection without maintaining a second definition.
70
71
 
71
- ## Requests, configuration and artifacts
72
+ ## Algorithm configuration
73
+
74
+ Configuration answers **how this algorithm behaves**. A request answers **what one invocation reads
75
+ and writes**. An artifact reference identifies **persisted data or fitted state**. These are separate
76
+ objects; sharing serialization does not make them one hierarchy.
77
+
78
+ The algorithm instance owns its configuration. Hyperparameters belong here, not in step requests.
79
+ `ExecutionOptions` belongs to each request and holds the invocation's seed and overwrite policy.
72
80
 
73
81
  ```mermaid
74
82
  classDiagram
83
+ class Algorithm {
84
+ <<abstract>>
85
+ config: AlgorithmConfig
86
+ }
75
87
  class AlgorithmConfig {
76
88
  <<frozen dataclass>>
77
- additional_predicates_file
89
+ additional_predicates_file: Path optional
78
90
  validate()
79
91
  from_dict(values)
80
92
  from_file(path)
@@ -87,74 +99,22 @@ classDiagram
87
99
  }
88
100
  class NNConfig {
89
101
  <<frozen dataclass>>
90
- capacity
91
- learning_rate
92
- batch_size
93
- weight_decay
94
- dropout
102
+ capacity: float
103
+ learning_rate: float
104
+ batch_size: int
105
+ weight_decay: float
106
+ dropout: float
95
107
  }
96
108
  class AlgorithmOwnedNNConfig {
97
109
  <<frozen dataclass>>
98
110
  architecture-specific defaults and settings
99
111
  }
100
- class StepRequest {
101
- <<frozen dataclass>>
102
- output
103
- execution
104
- predicates_file
105
- from_arguments(arguments)
106
- save(path)
107
- load(path)
108
- }
109
- class PredictSupervisedRequest {
110
- data
111
- algorithm
112
- index
113
- }
114
- class ArtifactReference {
115
- <<frozen dataclass>>
116
- directory
117
- kind
118
- open(directory)
119
- read_manifest()
120
- }
121
- class PreparedDataArtifact
122
- class SupervisedAlgorithmArtifact
123
- class PredictionsArtifact
124
- class PredictionIndex {
125
- directory
126
- splits
127
- read()
128
- }
129
- class ArtifactDestination {
130
- directory
131
- }
132
- class ExecutionOptions {
133
- seed
134
- overwrite
135
- }
112
+ Algorithm --> AlgorithmConfig : config
136
113
  AlgorithmConfig <|-- AlgorithmOwnedConfig
137
114
  AlgorithmConfig <|-- NNConfig
138
115
  NNConfig <|-- AlgorithmOwnedNNConfig
139
- StepRequest <|-- PredictSupervisedRequest
140
- ArtifactReference <|-- PreparedDataArtifact
141
- ArtifactReference <|-- SupervisedAlgorithmArtifact
142
- ArtifactReference <|-- PredictionsArtifact
143
- PredictSupervisedRequest --> PreparedDataArtifact : data
144
- PredictSupervisedRequest --> SupervisedAlgorithmArtifact : algorithm
145
- PredictSupervisedRequest --> PredictionIndex : index
146
- StepRequest --> ArtifactDestination : output
147
- StepRequest --> ExecutionOptions : execution
148
116
  ```
149
117
 
150
- Artifact constructors normalize paths without loading payloads. `open`/`read_manifest` validate the
151
- common envelope and artifact type. The runner validates references before algorithm code. Payload readers
152
- remain algorithm-owned; core imposes no tensor, tokenizer, batching or dataframe representation.
153
-
154
- `TrainingLabels.read` accepts only train/tuning. `PredictionIndex.read` projects keys at the I/O boundary.
155
- `PredictionContext` explicitly selects train/tuning and checks overlap with evaluated keys. Zero-shot
156
- requests carry `TaskSpec`. These interfaces make permissions clear; they are not a filesystem sandbox.
157
-
158
118
  Define configuration in the algorithm repository:
159
119
 
160
120
  ```python
@@ -304,6 +264,233 @@ interface covers configuration and its existing persisted provenance; it does no
304
264
  or count network parameters. Avoid adding another dataset-selection configuration until its upstream
305
265
  owner and identity format are agreed.
306
266
 
267
+ ## Step requests: typed method parameters
268
+
269
+ A request is an immutable **parameter object** for one method call. Its concrete type defines the
270
+ required inputs, optional inputs and output destination. The children represent different operations;
271
+ a preprocessing request does not turn into a training request. Every method has a concrete annotation,
272
+ for example `predict_supervised(request: PredictSupervisedRequest) -> PayloadMetadata`.
273
+
274
+ `StepRequest` supplies these common fields and methods to **all 13 concrete requests**:
275
+
276
+ ```mermaid
277
+ classDiagram
278
+ class StepRequest {
279
+ <<frozen dataclass>>
280
+ output: ArtifactDestination
281
+ execution: ExecutionOptions
282
+ predicates_file: Path optional
283
+ from_arguments(arguments)
284
+ artifact_inputs()
285
+ save(path)
286
+ load(path)
287
+ }
288
+ class ArtifactDestination {
289
+ directory: Path
290
+ }
291
+ class ExecutionOptions {
292
+ seed: int = 0
293
+ overwrite: bool = false
294
+ }
295
+ StepRequest --> ArtifactDestination : output
296
+ StepRequest --> ExecutionOptions : execution
297
+ ```
298
+
299
+ Callers instantiate concrete requests. `output_argument` and `result_type` are class metadata used to
300
+ map CLI arguments and construct the resulting artifact reference; they are not caller-supplied fields.
301
+ The following diagrams show every concrete request, split for readability. Each inherits the common
302
+ fields above. Relationships are inheritance (hollow arrow) or references to other objects (plain arrow).
303
+
304
+ ### Preprocessing and training requests
305
+
306
+ ```mermaid
307
+ classDiagram
308
+ StepRequest <|-- PreprocessDataRequest
309
+ StepRequest <|-- PretrainRequest
310
+ StepRequest <|-- TrainSupervisedRequest
311
+ StepRequest <|-- TrainFinetuneRequest
312
+ StepRequest <|-- TrainProbeRequest
313
+ class PreprocessDataRequest {
314
+ meds_dir: Path
315
+ result_type: PreparedDataArtifact
316
+ }
317
+ class PretrainRequest {
318
+ data: PreparedDataArtifact
319
+ result_type: PretrainedAlgorithmArtifact
320
+ }
321
+ class TrainSupervisedRequest {
322
+ data: PreparedDataArtifact
323
+ labels: TrainingLabels
324
+ result_type: SupervisedAlgorithmArtifact
325
+ }
326
+ class TrainFinetuneRequest {
327
+ data: PreparedDataArtifact
328
+ pretrained: PretrainedAlgorithmArtifact
329
+ labels: TrainingLabels
330
+ result_type: FinetunedAlgorithmArtifact
331
+ }
332
+ class TrainProbeRequest {
333
+ embeddings: EmbeddingsArtifact
334
+ labels: TrainingLabels
335
+ result_type: ProbeAlgorithmArtifact
336
+ }
337
+ ```
338
+
339
+ ### Embedding inference and fitted prediction requests
340
+
341
+ ```mermaid
342
+ classDiagram
343
+ StepRequest <|-- InferEmbeddingsRequest
344
+ StepRequest <|-- PredictSupervisedRequest
345
+ StepRequest <|-- PredictFinetuneRequest
346
+ StepRequest <|-- PredictProbeRequest
347
+ class InferEmbeddingsRequest {
348
+ data: PreparedDataArtifact
349
+ pretrained: PretrainedAlgorithmArtifact
350
+ index: PredictionIndex
351
+ result_type: EmbeddingsArtifact
352
+ }
353
+ class PredictSupervisedRequest {
354
+ data: PreparedDataArtifact
355
+ fitted_algorithm: SupervisedAlgorithmArtifact
356
+ index: PredictionIndex
357
+ result_type: PredictionsArtifact
358
+ }
359
+ class PredictFinetuneRequest {
360
+ data: PreparedDataArtifact
361
+ fitted_algorithm: FinetunedAlgorithmArtifact
362
+ index: PredictionIndex
363
+ result_type: PredictionsArtifact
364
+ }
365
+ class PredictProbeRequest {
366
+ embeddings: EmbeddingsArtifact
367
+ fitted_algorithm: ProbeAlgorithmArtifact
368
+ index: PredictionIndex
369
+ result_type: PredictionsArtifact
370
+ }
371
+ ```
372
+
373
+ `fitted_algorithm` references saved fitted state; it is not the Python algorithm instance. The CLI
374
+ names remain `input_supervised_algorithm_dir`, `input_finetuned_algorithm_dir` and
375
+ `input_probe_algorithm_dir`. These three requests use serialization schema version 2; version 1
376
+ request envelopes with the old `algorithm` field are rejected rather than silently reinterpreted.
377
+
378
+ ### Trajectory inference, zero-shot and predict-only requests
379
+
380
+ ```mermaid
381
+ classDiagram
382
+ StepRequest <|-- InferTrajectoriesRequest
383
+ StepRequest <|-- PredictZeroShotDirectRequest
384
+ StepRequest <|-- PredictZeroShotMaterializedRequest
385
+ StepRequest <|-- PredictOnlyRequest
386
+ class InferTrajectoriesRequest {
387
+ data: PreparedDataArtifact
388
+ pretrained: PretrainedAlgorithmArtifact
389
+ index: PredictionIndex
390
+ result_type: TrajectoriesArtifact
391
+ }
392
+ class PredictZeroShotDirectRequest {
393
+ data: PreparedDataArtifact
394
+ pretrained: PretrainedAlgorithmArtifact
395
+ index: PredictionIndex
396
+ task: TaskSpec
397
+ result_type: PredictionsArtifact
398
+ }
399
+ class PredictZeroShotMaterializedRequest {
400
+ data: PreparedDataArtifact
401
+ trajectories: TrajectoriesArtifact
402
+ meds_dir: Path
403
+ index: PredictionIndex
404
+ task: TaskSpec
405
+ result_type: PredictionsArtifact
406
+ }
407
+ class PredictOnlyRequest {
408
+ data: PreparedDataArtifact
409
+ index: PredictionIndex
410
+ context: PredictionContext optional
411
+ task: TaskSpec optional
412
+ result_type: PredictionsArtifact
413
+ }
414
+ ```
415
+
416
+ `TrainingLabels.read` accepts only train/tuning. `PredictionIndex.read` projects keys at the I/O boundary.
417
+ `PredictionContext` explicitly selects train/tuning and checks overlap with evaluated keys. Zero-shot
418
+ requests carry `TaskSpec`. These interfaces make permitted inputs clear; they are not a filesystem sandbox.
419
+ All requests inherit optional `predicates_file`; preprocessing uses feature predicates and zero-shot
420
+ prediction uses task predicates. Inference has no training-label field. Evaluation truth is a runner-only
421
+ argument and never enters a prediction request.
422
+
423
+ ### Constructing and executing a request
424
+
425
+ Given an implemented `MyAlgorithm(SupervisedAlgorithm)` and its `MyAlgorithmConfig`, a caller uses:
426
+
427
+ ```python
428
+ from pathlib import Path
429
+ from meds_algorithm import (
430
+ ArtifactDestination,
431
+ PreparedDataArtifact,
432
+ StepRunner,
433
+ SupervisedAlgorithmArtifact,
434
+ )
435
+ from meds_algorithm.requests import PredictionIndex, PredictSupervisedRequest
436
+
437
+ algorithm = MyAlgorithm(MyAlgorithmConfig()) # implementation and its hyperparameters
438
+ request = PredictSupervisedRequest(
439
+ data=PreparedDataArtifact(Path("prepared")),
440
+ fitted_algorithm=SupervisedAlgorithmArtifact(Path("trained")),
441
+ index=PredictionIndex(Path("index"), ("held_out",)),
442
+ output=ArtifactDestination(Path("predictions")),
443
+ )
444
+ predictions = StepRunner().run(
445
+ algorithm,
446
+ "predict_supervised",
447
+ request,
448
+ profile="supervised",
449
+ )
450
+ ```
451
+
452
+ Required keyword fields, type validation and semantic checks make the method boundary explicit. Frozen
453
+ requests cannot be modified in place. The runner substitutes a private staging destination in a copy;
454
+ the algorithm writes to `request.output.directory`. Configuration remains on `algorithm.config`.
455
+ Both objects are persisted separately as `config.json` and `request.json` for reproducibility.
456
+
457
+ ## Artifact references and outputs
458
+
459
+ Artifacts are persisted results shared between independent invocations. References identify their
460
+ directory and expected kind; they do not hold a loaded neural network or preprocessing payload.
461
+
462
+ ```mermaid
463
+ classDiagram
464
+ class ArtifactReference {
465
+ <<frozen dataclass>>
466
+ directory: Path
467
+ kind: ArtifactType
468
+ open(directory)
469
+ read_manifest()
470
+ }
471
+ ArtifactReference <|-- PreparedDataArtifact
472
+ ArtifactReference <|-- PretrainedAlgorithmArtifact
473
+ ArtifactReference <|-- SupervisedAlgorithmArtifact
474
+ ArtifactReference <|-- FinetunedAlgorithmArtifact
475
+ ArtifactReference <|-- ProbeAlgorithmArtifact
476
+ ArtifactReference <|-- EmbeddingsArtifact
477
+ ArtifactReference <|-- TrajectoriesArtifact
478
+ ArtifactReference <|-- PredictionsArtifact
479
+ class PayloadMetadata {
480
+ format: str
481
+ version: int = 1
482
+ }
483
+ PredictSupervisedRequest --> PreparedDataArtifact : data
484
+ PredictSupervisedRequest --> SupervisedAlgorithmArtifact : fitted_algorithm
485
+ PredictSupervisedRequest --> PredictionIndex : index
486
+ ```
487
+
488
+ Constructors normalize paths without loading payloads. `open`/`read_manifest` validate the common
489
+ envelope and artifact type. The runner validates references before algorithm code. Payload readers
490
+ remain algorithm-owned; core imposes no tensor, tokenizer, batching or dataframe representation.
491
+ Methods return `PayloadMetadata` describing what they wrote. The runner returns the published typed
492
+ artifact reference; metadata and the reference are different objects.
493
+
307
494
  ## Shared execution
308
495
 
309
496
  `AlgorithmRepository` associates profile names with implementation classes, creates a fresh instance
@@ -338,7 +525,7 @@ sequenceDiagram
338
525
  Algorithm methods write payloads only into `request.output.directory` and return `PayloadMetadata`. They
339
526
  must not write the reserved `manifest.yaml`, `config.json` or `request.json`, or publish final directories.
340
527
  Directly calling algorithm methods bypasses runner validation/publication; production and test adapters
341
- must use the runner. Low-level publication helpers remain available for legacy implementations.
528
+ must use the runner. The runner uses low-level publication helpers internally.
342
529
 
343
530
  Prediction payloads are Parquet shards with keys and probabilities, without ground truth. The runner
344
531
  validates their combined key set and probabilities, then optionally attaches evaluation labels. That
@@ -369,8 +556,81 @@ Replaying evaluation additionally requires the runner-only label path recorded i
369
556
  | Template | Algorithm/config stubs, CLI, profile YAML, test adapters and operational files |
370
557
  | Algorithm repository | Concrete typed config, algorithms/components, payload formats, compatibility and scientific tests |
371
558
 
372
- Legacy `Command`/`LegacyAlgorithmRepository` retain their previous mapping-based behavior. They do not acquire
373
- the runner's stronger validation/publication automatically. New generated repositories use
374
- `AlgorithmRepository`. Contract version 4 uses algorithm-named package imports, CLI entry points,
375
- artifact types and input/output arguments. Version 3 artifacts and serialized types are not accepted
376
- under the new names by changing a version field. Existing repositories require an explicit migration.
559
+ `AlgorithmRepository` is the only repository dispatcher. It constructs typed requests and delegates
560
+ to `StepRunner`; the former mapping-based command dispatcher has been removed. Fitted inputs must
561
+ record the complete resolved configuration; the default validator does not fill missing historical fields.
562
+ Contract version 4 uses algorithm-named package imports, CLI entry points, artifact types and arguments.
563
+ Existing repositories and incompatible serialized requests require an explicit migration.
564
+
565
+ ## Classes and functional helpers
566
+
567
+ Objects own meaningful responsibilities: algorithms own configuration and implementation methods,
568
+ requests own typed invocation parameters, references own artifact identity, and the runner owns execution.
569
+ `StepSpec.from_request_type` is a class factory because its purpose is to construct a specification.
570
+
571
+ Small stateless operations such as file hashing, encoding/decoding and conformance assertions remain
572
+ functions. `staged_directory` and `measured_artifact` are context managers: their local state exists only
573
+ between entry and exit, with cleanup and rollback on failure. A class wrapping these functions would
574
+ not improve their current lifecycle. `_ProcessTreeMemorySampler` is a class because it manages a
575
+ background sampler with start/stop state. The design does not require every function to become a method.
576
+
577
+ ## Shared acceptance tests
578
+
579
+ The optional `meds-algorithm[conformance]` extra supplies `AlgorithmConformanceTests`.
580
+ A generated test class inherits its eight tests; ordinary pytest fixtures connect the suite to a
581
+ concrete repository. No production algorithm inherits from the testing classes.
582
+
583
+ ```mermaid
584
+ classDiagram
585
+ class AlgorithmConformanceTests
586
+ class TestConformance
587
+ class AlgorithmCase {
588
+ root: Path
589
+ name: str
590
+ configurations: Sequence[AlgorithmConfig]
591
+ stub_profiles: Set[str]
592
+ profiles: tuple[str, ...]
593
+ create_repository() AlgorithmRepository
594
+ run_e2e() E2EResult
595
+ fitted_codes() Set[str]
596
+ prepare_meds_dev_run() MEDSDevRun
597
+ }
598
+ AlgorithmConformanceTests <|-- TestConformance
599
+ AlgorithmConformanceTests ..> AlgorithmCase : algorithm_case fixture
600
+ AlgorithmCase ..> AlgorithmRepository : factory derives profiles
601
+ AlgorithmCase ..> AlgorithmConfig : representative configurations
602
+ AlgorithmCase ..> E2EResult : execution adapter
603
+ AlgorithmCase ..> MEDSDevRun : integration adapter
604
+ ```
605
+
606
+ The generated `profile` fixture parametrizes registered profiles. `AlgorithmCase` only describes
607
+ inputs/adapters; it does not execute training or persist fitted state. `ProfileRunner` and
608
+ `MEDSDevPreparer` are callable typing protocols, not additional required implementation base classes.
609
+ Scientific tests and payload inspection stay in the algorithm repository. Shared test behavior and
610
+ its canonical inventory live in the packaged `TESTING.md`, rendered by the template documentation.
611
+
612
+ ## Profile orchestration (prototype)
613
+
614
+ The optional orchestration layer runs above the unchanged typed execution boundary. `RunConfig`
615
+ is a Hydra input schema; the concrete algorithm Config remains authoritative for algorithm settings.
616
+ `RunPlanner` derives artifact wiring from `StepSpec`/request types and validates before creating outputs.
617
+
618
+ ```mermaid
619
+ classDiagram
620
+ RunPlanner ..> RunConfig : resolves
621
+ RunPlanner --> RunPlan : creates without writing
622
+ RunPlan *-- StagePlan
623
+ RunCoordinator ..> RunPlan : persists
624
+ RunCoordinator --> LocalExecution
625
+ RunCoordinator --> SubmititExecution
626
+ LocalExecution ..> StageJob
627
+ SubmititExecution ..> StageJob : submits with dependencies
628
+ StageJob ..> AlgorithmRepository : invokes existing CLI in fresh process
629
+ AlgorithmRepository --> StepRunner
630
+ ```
631
+
632
+ A `StageJob` contains resolved CLI arguments and paths, never a fitted algorithm object.
633
+ Local execution waits for each subprocess; SLURM submits the chain and returns a job-ID receipt.
634
+ The same `RunPlan` is printed by dry-run or executed. Scheduling settings are persisted in `plan.json`,
635
+ while algorithm configs/requests remain in each artifact manifest. The bundled `ORCHESTRATION.md`
636
+ describes supported inputs, resource settings and limitations; the template embeds that same guide.
@@ -1,7 +1,36 @@
1
+ Metadata-Version: 2.5
2
+ Name: meds-algorithm
3
+ Version: 0.4.0a5
4
+ Summary: Shared MEDS algorithm contracts and conformance boundaries.
5
+ Project-URL: Repository, https://github.com/McDermottHealthAI/meds-algorithm
6
+ Project-URL: Documentation, https://github.com/McDermottHealthAI/meds-algorithm/blob/main/ARCHITECTURE.md
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: pyyaml>=6
11
+ Provides-Extra: conformance
12
+ Requires-Dist: meds-evaluation==0.0.3; extra == 'conformance'
13
+ Requires-Dist: polars>=1; extra == 'conformance'
14
+ Requires-Dist: pyarrow>=15; extra == 'conformance'
15
+ Requires-Dist: pytest>=8; extra == 'conformance'
16
+ Provides-Extra: data
17
+ Requires-Dist: polars>=1; extra == 'data'
18
+ Provides-Extra: orchestration
19
+ Requires-Dist: hydra-core<1.4,>=1.3.2; extra == 'orchestration'
20
+ Requires-Dist: submitit<2,>=1.5.2; extra == 'orchestration'
21
+ Provides-Extra: test
22
+ Requires-Dist: hydra-core<1.4,>=1.3.2; extra == 'test'
23
+ Requires-Dist: meds-evaluation==0.0.3; extra == 'test'
24
+ Requires-Dist: polars>=1; extra == 'test'
25
+ Requires-Dist: pyarrow>=15; extra == 'test'
26
+ Requires-Dist: pytest>=8; extra == 'test'
27
+ Requires-Dist: submitit<2,>=1.5.2; extra == 'test'
28
+ Description-Content-Type: text/markdown
29
+
1
30
  # meds-algorithm
2
31
 
3
32
  [![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-blue?logo=python&logoColor=white)](https://www.python.org/downloads/)
4
- [![PyPI: unreleased](https://img.shields.io/badge/PyPI-unreleased-lightgrey)](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/RELEASING.md)
33
+ [![PyPI](https://img.shields.io/pypi/v/meds-algorithm)](https://pypi.org/project/meds-algorithm/)
5
34
  [![Tests and code quality](https://github.com/McDermottHealthAI/meds-algorithm/actions/workflows/ci.yml/badge.svg)](https://github.com/McDermottHealthAI/meds-algorithm/actions/workflows/ci.yml)
6
35
  [![Documentation](https://img.shields.io/badge/docs-architecture-blue)](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/ARCHITECTURE.md)
7
36
  [![Contributors](https://img.shields.io/github/contributors/McDermottHealthAI/meds-algorithm)](https://github.com/McDermottHealthAI/meds-algorithm/graphs/contributors)
@@ -29,7 +58,8 @@ src/meds_algorithm/
29
58
  ├── configuration.py # AlgorithmConfig, NNConfig and execution options
30
59
  ├── serialization.py # strict, versioned config/request JSON serialization
31
60
  ├── runner.py # validate, execute, measure, publish; CLI adapter
32
- ├── contracts/commands.py # derived compatibility views and legacy dispatch
61
+ ├── orchestration/ # optional Hydra planning, local subprocesses and Submitit jobs
62
+ ├── contracts/commands.py # derived step specifications and validation
33
63
  ├── artifacts/
34
64
  │ ├── references.py # lazy typed artifact references and payload metadata
35
65
  │ ├── manifest.py # measured artifacts, envelope reading and digests
@@ -50,13 +80,13 @@ src/meds_algorithm/
50
80
 
51
81
  ## Installation and dependencies
52
82
 
53
- Install a reviewed revision with
54
- `pip install 'meds-algorithm @ git+https://github.com/McDermottHealthAI/meds-algorithm.git@<full-commit-sha>'`.
55
- The template currently records the exact SHA. For package-index release checks and publishing setup,
83
+ Install from PyPI with `pip install 'meds-algorithm[data]'`. Pin a released version in algorithm
84
+ repositories for reproducible environments. For package-index release checks and publishing setup,
56
85
  see the [release guide](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/RELEASING.md).
57
86
 
58
87
  - Base package: PyYAML; no dataframe engine, test runner, GPU or algorithm framework is imported by base APIs.
59
88
  - `data` extra: Polars for index/target reads and prediction validation.
89
+ - `orchestration` extra: Hydra configuration and Submitit; see the [prototype runner guide](src/meds_algorithm/ORCHESTRATION.md).
60
90
  - `conformance` extra: pytest, Polars, PyArrow and the pinned MEDS evaluation schema implementation.
61
91
  - `test` extra: dependencies for developing and testing this package.
62
92
 
@@ -91,8 +121,7 @@ layout into that interface; these upstream names are not part of this package's
91
121
 
92
122
  ## Publication, provenance and measurement
93
123
 
94
- Pass the algorithm checkout explicitly to `StepRunner(repo=algorithm_root)` or, for legacy implementations,
95
- `measured_artifact(..., repo=algorithm_root)`. The package never
124
+ Pass the algorithm checkout explicitly to `StepRunner(repo=algorithm_root)`. The package never
96
125
  infers the algorithm from its own installation path or the working directory. Without a supplied checkout,
97
126
  commit and dirty state are unknown (`null`); wheel deployments must retain algorithm build provenance
98
127
  separately rather than inventing a checkout revision.
@@ -112,6 +141,8 @@ values as `null`. GPU instrumentation remains the algorithm/framework's responsi
112
141
 
113
142
  ## Validation
114
143
 
144
+ See [the test catalogue](src/meds_algorithm/TESTING.md) for what each test checks, why it exists, and the limits of the suite.
145
+
115
146
  ```bash
116
147
  uv sync --extra test --group dev
117
148
  uv run ruff check .
@@ -132,6 +163,21 @@ training. Keep each algorithm's data-access and cluster approval rules in its ow
132
163
 
133
164
  ## Changes
134
165
 
166
+ ### 0.4.0a5 (2026-11-03)
167
+
168
+ - Add an optional profile-runner prototype: shared Hydra schema, resolved dry-run plans, local
169
+ subprocess execution and dependent Submitit SLURM jobs. Existing per-step APIs remain unchanged.
170
+
171
+ - Ship an opt-in `AlgorithmConformanceTests` acceptance suite and typed `AlgorithmCase` adapter;
172
+ generated repositories inherit the checks and retain algorithm-specific scientific tests.
173
+ - Remove `Command` and `LegacyAlgorithmRepository`; all algorithm execution uses typed profiles and the runner.
174
+ - Rename fitted prediction request fields to `fitted_algorithm` and bump those request schemas to 2.
175
+ External CLI argument names stay unchanged. Old request envelopes require explicit migration.
176
+ - Require complete recorded fitted-input configuration instead of filling missing historical fields.
177
+ - Expose `StepSpec.from_request_type` as the typed specification factory.
178
+ - Separate configuration, every concrete request type, artifacts and execution in the UML guide;
179
+ include the per-test catalogue in the installed package.
180
+
135
181
  ### 0.4.0a4
136
182
 
137
183
  - Rename the repository/distribution from `meds-algorithm-core` to `meds-algorithm`, the Python package
@@ -1,31 +1,7 @@
1
- Metadata-Version: 2.5
2
- Name: meds-algorithm
3
- Version: 0.4.0a4
4
- Summary: Shared MEDS algorithm contracts and conformance boundaries.
5
- Project-URL: Repository, https://github.com/McDermottHealthAI/meds-algorithm
6
- Project-URL: Documentation, https://github.com/McDermottHealthAI/meds-algorithm/blob/main/ARCHITECTURE.md
7
- License-Expression: MIT
8
- License-File: LICENSE
9
- Requires-Python: >=3.11
10
- Requires-Dist: pyyaml>=6
11
- Provides-Extra: conformance
12
- Requires-Dist: meds-evaluation==0.0.3; extra == 'conformance'
13
- Requires-Dist: polars>=1; extra == 'conformance'
14
- Requires-Dist: pyarrow>=15; extra == 'conformance'
15
- Requires-Dist: pytest>=8; extra == 'conformance'
16
- Provides-Extra: data
17
- Requires-Dist: polars>=1; extra == 'data'
18
- Provides-Extra: test
19
- Requires-Dist: meds-evaluation==0.0.3; extra == 'test'
20
- Requires-Dist: polars>=1; extra == 'test'
21
- Requires-Dist: pyarrow>=15; extra == 'test'
22
- Requires-Dist: pytest>=8; extra == 'test'
23
- Description-Content-Type: text/markdown
24
-
25
1
  # meds-algorithm
26
2
 
27
3
  [![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-blue?logo=python&logoColor=white)](https://www.python.org/downloads/)
28
- [![PyPI: unreleased](https://img.shields.io/badge/PyPI-unreleased-lightgrey)](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/RELEASING.md)
4
+ [![PyPI](https://img.shields.io/pypi/v/meds-algorithm)](https://pypi.org/project/meds-algorithm/)
29
5
  [![Tests and code quality](https://github.com/McDermottHealthAI/meds-algorithm/actions/workflows/ci.yml/badge.svg)](https://github.com/McDermottHealthAI/meds-algorithm/actions/workflows/ci.yml)
30
6
  [![Documentation](https://img.shields.io/badge/docs-architecture-blue)](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/ARCHITECTURE.md)
31
7
  [![Contributors](https://img.shields.io/github/contributors/McDermottHealthAI/meds-algorithm)](https://github.com/McDermottHealthAI/meds-algorithm/graphs/contributors)
@@ -53,7 +29,8 @@ src/meds_algorithm/
53
29
  ├── configuration.py # AlgorithmConfig, NNConfig and execution options
54
30
  ├── serialization.py # strict, versioned config/request JSON serialization
55
31
  ├── runner.py # validate, execute, measure, publish; CLI adapter
56
- ├── contracts/commands.py # derived compatibility views and legacy dispatch
32
+ ├── orchestration/ # optional Hydra planning, local subprocesses and Submitit jobs
33
+ ├── contracts/commands.py # derived step specifications and validation
57
34
  ├── artifacts/
58
35
  │ ├── references.py # lazy typed artifact references and payload metadata
59
36
  │ ├── manifest.py # measured artifacts, envelope reading and digests
@@ -74,13 +51,13 @@ src/meds_algorithm/
74
51
 
75
52
  ## Installation and dependencies
76
53
 
77
- Install a reviewed revision with
78
- `pip install 'meds-algorithm @ git+https://github.com/McDermottHealthAI/meds-algorithm.git@<full-commit-sha>'`.
79
- The template currently records the exact SHA. For package-index release checks and publishing setup,
54
+ Install from PyPI with `pip install 'meds-algorithm[data]'`. Pin a released version in algorithm
55
+ repositories for reproducible environments. For package-index release checks and publishing setup,
80
56
  see the [release guide](https://github.com/McDermottHealthAI/meds-algorithm/blob/main/RELEASING.md).
81
57
 
82
58
  - Base package: PyYAML; no dataframe engine, test runner, GPU or algorithm framework is imported by base APIs.
83
59
  - `data` extra: Polars for index/target reads and prediction validation.
60
+ - `orchestration` extra: Hydra configuration and Submitit; see the [prototype runner guide](src/meds_algorithm/ORCHESTRATION.md).
84
61
  - `conformance` extra: pytest, Polars, PyArrow and the pinned MEDS evaluation schema implementation.
85
62
  - `test` extra: dependencies for developing and testing this package.
86
63
 
@@ -115,8 +92,7 @@ layout into that interface; these upstream names are not part of this package's
115
92
 
116
93
  ## Publication, provenance and measurement
117
94
 
118
- Pass the algorithm checkout explicitly to `StepRunner(repo=algorithm_root)` or, for legacy implementations,
119
- `measured_artifact(..., repo=algorithm_root)`. The package never
95
+ Pass the algorithm checkout explicitly to `StepRunner(repo=algorithm_root)`. The package never
120
96
  infers the algorithm from its own installation path or the working directory. Without a supplied checkout,
121
97
  commit and dirty state are unknown (`null`); wheel deployments must retain algorithm build provenance
122
98
  separately rather than inventing a checkout revision.
@@ -136,6 +112,8 @@ values as `null`. GPU instrumentation remains the algorithm/framework's responsi
136
112
 
137
113
  ## Validation
138
114
 
115
+ See [the test catalogue](src/meds_algorithm/TESTING.md) for what each test checks, why it exists, and the limits of the suite.
116
+
139
117
  ```bash
140
118
  uv sync --extra test --group dev
141
119
  uv run ruff check .
@@ -156,6 +134,21 @@ training. Keep each algorithm's data-access and cluster approval rules in its ow
156
134
 
157
135
  ## Changes
158
136
 
137
+ ### 0.4.0a5 (2026-11-03)
138
+
139
+ - Add an optional profile-runner prototype: shared Hydra schema, resolved dry-run plans, local
140
+ subprocess execution and dependent Submitit SLURM jobs. Existing per-step APIs remain unchanged.
141
+
142
+ - Ship an opt-in `AlgorithmConformanceTests` acceptance suite and typed `AlgorithmCase` adapter;
143
+ generated repositories inherit the checks and retain algorithm-specific scientific tests.
144
+ - Remove `Command` and `LegacyAlgorithmRepository`; all algorithm execution uses typed profiles and the runner.
145
+ - Rename fitted prediction request fields to `fitted_algorithm` and bump those request schemas to 2.
146
+ External CLI argument names stay unchanged. Old request envelopes require explicit migration.
147
+ - Require complete recorded fitted-input configuration instead of filling missing historical fields.
148
+ - Expose `StepSpec.from_request_type` as the typed specification factory.
149
+ - Separate configuration, every concrete request type, artifacts and execution in the UML guide;
150
+ include the per-test catalogue in the installed package.
151
+
159
152
  ### 0.4.0a4
160
153
 
161
154
  - Rename the repository/distribution from `meds-algorithm-core` to `meds-algorithm`, the Python package