expops 0.1.16.dev0__tar.gz → 0.1.17.dev0__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.
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/PKG-INFO +1 -1
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/data-parallelism.md +9 -10
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/pipelines.md +7 -5
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/reporting.md +9 -32
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/seed-parallelism.md +15 -14
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/configuration.md +8 -3
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/overview.md +0 -2
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.js +18 -11
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.py +7 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/configs/project_config.yaml +21 -21
- expops-0.1.17.dev0/premier-league/firestore.json +1 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/PKG-INFO +1 -1
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/SOURCES.txt +15 -9
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/_version.py +3 -3
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/custom/custom_adapter.py +27 -12
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/executor_worker.py +127 -15
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/networkx_parser.py +35 -11
- expops-0.1.17.dev0/src/mlops/core/pipeline_tree.py +524 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/pipeline_utils.py +34 -5
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/probe_path_selectors.py +6 -1
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/process_hashing.py +19 -5
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/step_state_manager.py +80 -17
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/step_system.py +3 -5
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/managers/project_manager.py +91 -11
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/platform.py +274 -12
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/entrypoint.py +61 -2
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/factory.py +26 -12
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/configs/project_config.yaml +3 -3
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/server.py +46 -13
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/mlops-charts.js +64 -13
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/script.js +124 -26
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/testing-plan.md +17 -7
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_executor_worker.py +72 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_graph_expansion.py +29 -0
- expops-0.1.17.dev0/tests/unit/test_core/test_networkx_parser_code_field.py +78 -0
- expops-0.1.17.dev0/tests/unit/test_core/test_pipeline_tree_steps.py +101 -0
- expops-0.1.17.dev0/tests/unit/test_core/test_probe_path_selectors_xpath.py +81 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_process_hashing.py +1 -1
- expops-0.1.17.dev0/tests/unit/test_core/test_step_state_manager.py +90 -0
- expops-0.1.17.dev0/tests/unit/test_platform/test_dynamic_js_charts.py +146 -0
- expops-0.1.17.dev0/tests/unit/test_platform/test_project_metadata_resilience.py +94 -0
- expops-0.1.17.dev0/tests/unit/test_reporting/test_entrypoint.py +53 -0
- expops-0.1.16.dev0/docs/project-structure/charts.md +0 -160
- expops-0.1.16.dev0/src/mlops/core/pipeline_tree.py +0 -234
- expops-0.1.16.dev0/tests/unit/test_core/test_step_state_manager.py +0 -49
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.github/workflows/ci.yml +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.github/workflows/release.yml +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.gitignore +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/LICENSE +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/README.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/advanced/backends.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/caching.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/distributed.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/environments.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/getting-started/creating-a-project.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/getting-started/quick-start.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/index.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/model-code.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/requirements.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/templates/premier-league.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/templates/sklearn-basic.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/web-ui/local-ui.md +0 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/requirements.txt +0 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/configs/compute_config.yaml +0 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/data/England CSV.csv +0 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/models/premier_league_model.py +0 -0
- {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/requirements.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/pyproject.toml +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/setup.cfg +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/__main__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/core/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/main.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/context.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/registry.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/web/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/web/server.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/dependency_links.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/entry_points.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/requires.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/top_level.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/__main__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/base.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/config_schema.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/custom/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/plugin_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/sklearn/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/sklearn/adapter.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/controller.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/process_runner.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/providers.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/compute_config.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/custom_model_base.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/dask_networkx_executor.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/data_hashing.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/graph_expansion.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/graph_types.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/payload_spill.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/workspace.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/base.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/conda_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/factory.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/pyenv_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/setup_env.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/system_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/utils.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/venv_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/main.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/managers/reproducibility_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/context.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/kv_utils.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/registry.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/context.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/env_export.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/gcp_kv_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/gcs_object_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/memory_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/redis_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/sqlite_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/interfaces/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/interfaces/kv_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/path_utils.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/README.md +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/charts/plot_metrics.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/charts/requirements.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/configs/compute_config.yaml +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/data/train.csv +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/models/model.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/requirements.txt +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/index.html +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/styles.css +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/conftest.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_dask_networkx_executor.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_data_hashing.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_networkx_parser.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_payload_spill.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_prepare_runner_kwargs.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_step_system.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_managers/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_managers/test_reproducibility_manager.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/__init__.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_factory.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_gcp_kv_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_gcs_object_store.py +0 -0
- {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_sqlite_store.py +0 -0
|
@@ -5,7 +5,7 @@ Use it when a process returns row-splittable data (pandas DataFrame, numpy array
|
|
|
5
5
|
|
|
6
6
|
## Config
|
|
7
7
|
|
|
8
|
-
### Split from upstream (no `
|
|
8
|
+
### Split from upstream (no `code`)
|
|
9
9
|
|
|
10
10
|
```yaml
|
|
11
11
|
processes:
|
|
@@ -17,16 +17,16 @@ processes:
|
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
- `data_name` tells the system which upstream output key to split.
|
|
20
|
-
- The split process has no `
|
|
21
|
-
-
|
|
20
|
+
- The split process has no `code`; it will merge its upstream outputs and split the `data_name` key.
|
|
21
|
+
- The script path for such helper split nodes is optional.
|
|
22
22
|
|
|
23
|
-
### User-defined splitter (`
|
|
23
|
+
### User-defined splitter (`code`)
|
|
24
24
|
|
|
25
25
|
```yaml
|
|
26
26
|
processes:
|
|
27
27
|
- name: "nn_data_parallel"
|
|
28
28
|
description: "Custom splitter"
|
|
29
|
-
|
|
29
|
+
code: "define_nn_data_parallel"
|
|
30
30
|
data_parallelism:
|
|
31
31
|
size: [50, 20, 20]
|
|
32
32
|
```
|
|
@@ -42,7 +42,7 @@ def define_nn_data_parallel():
|
|
|
42
42
|
|
|
43
43
|
- Return a **list of rows**; the framework will split it based on `size`.
|
|
44
44
|
- If `data_name` is omitted and the function returns a list, the data key defaults to `data`.
|
|
45
|
-
- `script_path` is required when using `
|
|
45
|
+
- `script_path` is required when using an explicit `code` function.
|
|
46
46
|
|
|
47
47
|
### Size formats
|
|
48
48
|
|
|
@@ -57,10 +57,10 @@ Add a dedicated aggregation process to collapse the **latest** data-parallel lay
|
|
|
57
57
|
processes:
|
|
58
58
|
- name: "aggregate_results"
|
|
59
59
|
data_aggregation: true
|
|
60
|
-
|
|
60
|
+
code: "define_aggregate_results"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Aggregation processes **must** define a `
|
|
63
|
+
Aggregation processes **must** define a `code` function reference.
|
|
64
64
|
|
|
65
65
|
### Input shape at aggregation
|
|
66
66
|
|
|
@@ -76,8 +76,7 @@ def define_aggregate_results(df):
|
|
|
76
76
|
## Notes
|
|
77
77
|
|
|
78
78
|
- Data parallelism duplicates downstream nodes in the graph and is visible in the UI.
|
|
79
|
-
- Chart probe paths can use selector syntax to automatically expand to data-parallel
|
|
80
|
-
partitions; see the chart documentation for details.
|
|
79
|
+
- Chart probe paths can use XPath selector syntax over the pipeline tree (including `@partition='p1'` predicates) to automatically expand to data-parallel partitions; see the chart documentation for details.
|
|
81
80
|
- Multiple data-parallel layers are supported. Each aggregation collapses only the
|
|
82
81
|
most recent data-parallel layer; outer layers are still represented by separate
|
|
83
82
|
process nodes.
|
|
@@ -46,12 +46,12 @@ Each process must be explicitly defined with its configuration:
|
|
|
46
46
|
processes:
|
|
47
47
|
- name: "feature_engineering"
|
|
48
48
|
description: "Load and prepare data"
|
|
49
|
-
|
|
49
|
+
code: "define_feature_engineering_process"
|
|
50
50
|
environment: "my-project-env"
|
|
51
51
|
|
|
52
52
|
- name: "train_model"
|
|
53
53
|
description: "Train the model"
|
|
54
|
-
|
|
54
|
+
code: "define_training_process"
|
|
55
55
|
environment: "my-project-env"
|
|
56
56
|
parameters:
|
|
57
57
|
learning_rate: 0.001
|
|
@@ -67,8 +67,10 @@ processes:
|
|
|
67
67
|
|
|
68
68
|
- `name`: Unique process identifier (must match names in `process_adjlist`)
|
|
69
69
|
- `description`: Human-readable description
|
|
70
|
-
- `
|
|
71
|
-
- `
|
|
70
|
+
- `code` (optional): Unified code reference for the process:
|
|
71
|
+
- `code: "script_key.function_name"` uses the script registered under `script_key` in the top-level `scripts` map and calls `function_name`.
|
|
72
|
+
- `code: "function_name"` uses the first script key in `scripts` and calls `function_name`.
|
|
73
|
+
- If omitted, ordinary processes default to a function with the same name as the process on the default script, while pure split/aggregation helper nodes can omit `code` entirely.
|
|
72
74
|
- `environment`: Environment name to use (defaults to the first environment if omitted)
|
|
73
75
|
- `parameters`: Optional parameters injected by name into the process function
|
|
74
76
|
- `type`: Optional type (e.g., `"chart"` for chart generation processes)
|
|
@@ -77,7 +79,7 @@ processes:
|
|
|
77
79
|
|
|
78
80
|
## Process Functions
|
|
79
81
|
|
|
80
|
-
Processes are implemented in Python using the `@process()` decorator. The function name
|
|
82
|
+
Processes are implemented in Python using the `@process()` decorator. The function name referenced in `code` (or the process name when `code` is omitted) must match the registered process:
|
|
81
83
|
|
|
82
84
|
```python
|
|
83
85
|
from expops.core import process, step,
|
|
@@ -69,8 +69,8 @@ reporting:
|
|
|
69
69
|
charts:
|
|
70
70
|
- name: "my_chart"
|
|
71
71
|
probe_paths:
|
|
72
|
-
train: "//*[@name
|
|
73
|
-
eval: "//*[@name
|
|
72
|
+
train: "//*[@name='train_model']"
|
|
73
|
+
eval: "//*[@name='evaluate_model']"
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
The chart function receives:
|
|
@@ -100,38 +100,15 @@ Common patterns:
|
|
|
100
100
|
|
|
101
101
|
| Goal | XPath pattern |
|
|
102
102
|
|------|----------------|
|
|
103
|
-
| Process by name | `//*[@name=
|
|
104
|
-
| Process + step | `//*[@name=
|
|
105
|
-
| Specific partition/seed | `//*[@partition=
|
|
106
|
-
| Any partition/seed | `//*[@partition]/*[@seed]/*[@name=
|
|
107
|
-
|
|
108
|
-
In `project_config.yaml`, probe paths are double-quoted YAML strings, so escape each `"` as `\"` (see examples below).
|
|
109
|
-
|
|
110
|
-
**Examples from projects:**
|
|
111
|
-
|
|
112
|
-
- **sklearn-basic** (simple pipeline, no steps):
|
|
113
|
-
|
|
114
|
-
```yaml
|
|
115
|
-
probe_paths:
|
|
116
|
-
train: "//*[@name=\"train_model\"]"
|
|
117
|
-
eval: "//*[@name=\"evaluate_model\"]"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
- **premier-league** (process + step; partition and seed):
|
|
121
|
-
|
|
122
|
-
```yaml
|
|
123
|
-
probe_paths:
|
|
124
|
-
feat: "//*[@name=\"feature_engineering_generic\"]/step[@name=\"feature_analysis\"]"
|
|
125
|
-
nn_a_p1_seed41: "//*[@partition=\"p1\"]/*[@seed=\"41\"]/*[@name=\"nn_training_a\"]/*[@name=\"train_and_evaluate_nn_classifier\"]"
|
|
126
|
-
linear: "//*[@partition]/*[@seed]/*[@name=\"linear_inference\"]/*[@name=\"test_inference_classification\"]"
|
|
127
|
-
nn_best: "//*[@partition]/*[@seed]/*[@name=\"nn_best_inference\"]/step[@name=\"test_inference_classification\"]"
|
|
128
|
-
ensemble: "//*[@partition]/*[@seed]/*[@name=\"ensemble_inference\"]"
|
|
129
|
-
```
|
|
103
|
+
| Process by name | `//*[@name='process_name']` |
|
|
104
|
+
| Process + step | `//*[@name='process_name']/*[@name='step_name']` or `//*[@name='step_name']` if the step name is unique among process names |
|
|
105
|
+
| Specific partition/seed | `//*[@partition='p1']/*[@seed='41']/*[@name='process_name']` |
|
|
106
|
+
| Any partition/seed | `//*[@partition]/*[@seed]/*[@name='process_name']` |
|
|
130
107
|
|
|
131
108
|
#### How keys map to chart metrics
|
|
132
109
|
|
|
133
110
|
- **One XPath match**: The config key is preserved (e.g. `train` → `metrics['train']`).
|
|
134
|
-
- **Multiple XPath matches**: Each resolved probe path becomes a key (e.g. `
|
|
111
|
+
- **Multiple XPath matches**: Each resolved probe path becomes a key. The key is the canonical XPath-style identifier for that process/step (e.g. `"//*[@partition='p1']/*[@seed='41']/*[@name='nn_training_a']/step[@name='train_and_evaluate_nn_classifier']"`). Chart code can iterate over keys or use prefix/grouping logic to aggregate across partitions or seeds.
|
|
135
112
|
- **Literal path**: Single key as in config (e.g. `train: "train_model"` → `metrics['train']`).
|
|
136
113
|
|
|
137
114
|
### Output
|
|
@@ -147,7 +124,7 @@ Dynamic charts provide real-time, interactive visualizations.
|
|
|
147
124
|
|
|
148
125
|
**Configuration**: Dynamic charts are defined as **pipeline processes** in `project_config.yaml` (under `experiment.parameters.pipeline.processes`). Each dynamic chart process must have:
|
|
149
126
|
|
|
150
|
-
- `
|
|
127
|
+
- `code` - a unified code reference that points to your JS chart script and function (e.g. `code: "reporting_js.nn_losses"`), where `reporting_js` is defined under `scripts:` at the top of the config
|
|
151
128
|
- `chart_type: "dynamic"`
|
|
152
129
|
- `probe_paths` - same XPath semantics as static charts (see [Probe paths](#probe-paths) below)
|
|
153
130
|
|
|
@@ -158,7 +135,7 @@ Dynamic charts provide real-time, interactive visualizations.
|
|
|
158
135
|
```yaml
|
|
159
136
|
# Under experiment.parameters.pipeline.processes:
|
|
160
137
|
- name: "nn_losses"
|
|
161
|
-
|
|
138
|
+
code: "reporting_js.nn_losses"
|
|
162
139
|
environment: "premier-league-env-reporting"
|
|
163
140
|
chart_type: "dynamic"
|
|
164
141
|
probe_paths: ...
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
### Seed Parallelism
|
|
2
2
|
|
|
3
3
|
Seed parallelism duplicates the downstream process graph and runs each duplicate under a different random seed. It uses deterministic task-level seeding so that each duplicated branch is reproducible.
|
|
4
4
|
|
|
5
5
|
## Config
|
|
6
6
|
|
|
7
|
-
### Predefined seed split (no `
|
|
7
|
+
### Predefined seed split (no `code`)
|
|
8
8
|
|
|
9
9
|
```yaml
|
|
10
10
|
processes:
|
|
@@ -14,18 +14,17 @@ processes:
|
|
|
14
14
|
seeds: [41, 42, 43]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
- The split process has no `
|
|
17
|
+
- The split process has no `code`; it will merge upstream outputs and pass them through.
|
|
18
18
|
- The engine duplicates all downstream nodes until a `seed_aggregation` node is reached.
|
|
19
|
-
- Duplicated nodes
|
|
20
|
-
- `script_path` is optional for split nodes with no `code_function`.
|
|
19
|
+
- Duplicated nodes are tracked via structured `seed_value` metadata on each process instance.
|
|
21
20
|
|
|
22
|
-
### User-defined seed split (`
|
|
21
|
+
### User-defined seed split (`code`)
|
|
23
22
|
|
|
24
23
|
```yaml
|
|
25
24
|
processes:
|
|
26
25
|
- name: "seed_parallel"
|
|
27
26
|
description: "Custom seed hook"
|
|
28
|
-
|
|
27
|
+
code: "define_seed_parallel_process"
|
|
29
28
|
seed_parallelism:
|
|
30
29
|
seeds: [41, 42, 43]
|
|
31
30
|
```
|
|
@@ -39,7 +38,6 @@ def define_seed_parallel_process(seeds, **inputs):
|
|
|
39
38
|
```
|
|
40
39
|
|
|
41
40
|
- The `seeds` list is passed to the user-defined function as a kwarg.
|
|
42
|
-
- `script_path` is required when using `code_function`.
|
|
43
41
|
|
|
44
42
|
## Aggregation
|
|
45
43
|
|
|
@@ -49,7 +47,7 @@ Add a dedicated aggregation process to collapse the **latest** seed-parallel lay
|
|
|
49
47
|
processes:
|
|
50
48
|
- name: "aggregate_seeds"
|
|
51
49
|
seed_aggregation: true
|
|
52
|
-
|
|
50
|
+
code: "define_aggregate_seeds"
|
|
53
51
|
```
|
|
54
52
|
|
|
55
53
|
### Input shape at aggregation
|
|
@@ -74,8 +72,11 @@ def define_aggregate_data_and_seed(metrics):
|
|
|
74
72
|
|
|
75
73
|
## Notes
|
|
76
74
|
|
|
77
|
-
- Seed
|
|
78
|
-
|
|
79
|
-
seed
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
- **UI representation**: Seed-parallel duplicates now use canonical XPath-style
|
|
76
|
+
process IDs in the process graph (for example
|
|
77
|
+
`//*[@partition='p1']/*[@seed='41']/process[@name='train']`), and human-readable
|
|
78
|
+
labels such as `train P1 S41` are derived from this metadata.
|
|
79
|
+
- **Task-level RNG seeding**: Uses the most recent (innermost) seed layer when
|
|
80
|
+
multiple seed-parallel layers are nested.
|
|
81
|
+
- **Aggregation behavior**: Each aggregation collapses only the most recent
|
|
82
|
+
seed-parallel layer; outer layers remain as separate process nodes.
|
|
@@ -41,8 +41,13 @@ reporting: # Chart entrypoints, probe paths, reporting environment
|
|
|
41
41
|
|
|
42
42
|
You can omit some process fields and rely on defaults to keep config minimal:
|
|
43
43
|
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
44
|
+
- **Scripts section**: The top-level `scripts` map still defines script keys to file paths. The **first key** is treated as the default script for processes that do not explicitly specify a script key in `code`.
|
|
45
|
+
- **Code field**: Each process may define a single `code` field instead of separate `script` and `code_function` fields:
|
|
46
|
+
- `code: "script_key.function_name"` → use the script registered under `script_key` and call `function_name` from that module.
|
|
47
|
+
- `code: "function_name"` → use the first script key in `scripts` and call `function_name`.
|
|
48
|
+
- **Omitted code**:
|
|
49
|
+
- For ordinary processes, if `code` is omitted the system assumes a function with the same name as the process, loaded from the default script.
|
|
50
|
+
- For data/seed split helper nodes (processes that only define `data_parallelism` or `seed_parallelism` and no `code`), the system treats them as function-less split nodes.
|
|
46
51
|
|
|
47
52
|
Example minimal process that uses both defaults:
|
|
48
53
|
|
|
@@ -88,7 +93,7 @@ For detailed information on each configuration section:
|
|
|
88
93
|
- **Process & Step Code**: [Model Code](model-code.md)
|
|
89
94
|
- **Caching**: [Caching & Reproducibility](../features/caching.md)
|
|
90
95
|
- **Backends**: [Backends](../advanced/backends.md)
|
|
91
|
-
- **Reporting/Charts**: [Reporting Features](../features/reporting.md)
|
|
96
|
+
- **Reporting/Charts**: [Reporting Features](../features/reporting.md)
|
|
92
97
|
- **Cluster Execution**: [Cluster Configuration](../advanced/cluster-config.md) and [Distributed Computing](../features/distributed.md)
|
|
93
98
|
|
|
94
99
|
## Example Configurations
|
|
@@ -49,8 +49,6 @@ The `charts/` directory contains visualization code:
|
|
|
49
49
|
- **plot_metrics.py**: Static PNG chart generation
|
|
50
50
|
- **plot_metrics.js**: Dynamic interactive charts
|
|
51
51
|
|
|
52
|
-
See [Chart Generation](charts.md) for details.
|
|
53
|
-
|
|
54
52
|
### Dependencies
|
|
55
53
|
|
|
56
54
|
- **requirements.txt**: Main dependencies for training/inference
|
{expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.js
RENAMED
|
@@ -17,16 +17,22 @@ chart('nn_losses', (probePaths, ctx, listener) => {
|
|
|
17
17
|
datasets: []
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
const formatProbeLabel = (
|
|
21
|
-
const raw = String(
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const formatProbeLabel = (canonicalPath) => {
|
|
21
|
+
const raw = String(canonicalPath || '');
|
|
22
|
+
const partMatch = raw.match(/@partition='p(\d+)'/);
|
|
23
|
+
const seedMatch = raw.match(/@seed='(\d+)'/);
|
|
24
|
+
const procMatch = raw.match(/process\[@name='([^']+)'\]/);
|
|
25
|
+
const parts = [];
|
|
26
|
+
if (procMatch && procMatch[1]) {
|
|
27
|
+
parts.push(procMatch[1]);
|
|
28
|
+
}
|
|
29
|
+
if (partMatch && partMatch[1]) {
|
|
30
|
+
parts.push(`P${partMatch[1]}`);
|
|
31
|
+
}
|
|
32
|
+
if (seedMatch && seedMatch[1]) {
|
|
33
|
+
parts.push(`S${seedMatch[1]}`);
|
|
34
|
+
}
|
|
35
|
+
return parts.join(' ') || raw;
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
const desiredOrder = [
|
|
@@ -42,8 +48,9 @@ chart('nn_losses', (probePaths, ctx, listener) => {
|
|
|
42
48
|
.filter(Boolean);
|
|
43
49
|
|
|
44
50
|
selectedKeys.forEach((k) => {
|
|
51
|
+
const canonical = probePaths[k];
|
|
45
52
|
chartData.datasets.push({
|
|
46
|
-
label: formatProbeLabel(
|
|
53
|
+
label: formatProbeLabel(canonical).replace(/_/g, ' ').toUpperCase(),
|
|
47
54
|
data: [],
|
|
48
55
|
borderColor: colors[colorIndex % colors.length],
|
|
49
56
|
backgroundColor: colors[colorIndex % colors.length] + '33',
|
{expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Dict, Any, Optional
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
import matplotlib
|
|
4
5
|
matplotlib.use('Agg')
|
|
@@ -7,6 +8,8 @@ import numpy as np
|
|
|
7
8
|
|
|
8
9
|
from mlops.reporting import chart, ChartContext
|
|
9
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
@chart()
|
|
12
15
|
def pca_scree(metrics: Dict[str, Any], ctx: ChartContext) -> None:
|
|
@@ -163,6 +166,10 @@ def test_metrics_comparison(metrics: Dict[str, Any], ctx: ChartContext) -> None:
|
|
|
163
166
|
f1s.append(f1 if f1 is not None else 0.0)
|
|
164
167
|
|
|
165
168
|
if not labels:
|
|
169
|
+
logger.warning(
|
|
170
|
+
"test_metrics_comparison: no comparable metrics found for chart generation. metric_keys=%s",
|
|
171
|
+
sorted((metrics or {}).keys()),
|
|
172
|
+
)
|
|
166
173
|
return
|
|
167
174
|
|
|
168
175
|
x = np.arange(len(labels))
|
|
@@ -95,21 +95,21 @@ experiment:
|
|
|
95
95
|
- name: "linear_training"
|
|
96
96
|
|
|
97
97
|
- name: "nn_training_a"
|
|
98
|
-
|
|
98
|
+
code: "define_nn_training_process"
|
|
99
99
|
parameters:
|
|
100
100
|
hidden_layers: [128, 64]
|
|
101
101
|
learning_rate: 0.002
|
|
102
102
|
epochs: 50
|
|
103
103
|
|
|
104
104
|
- name: "nn_training_b"
|
|
105
|
-
|
|
105
|
+
code: "define_nn_training_process"
|
|
106
106
|
parameters:
|
|
107
107
|
hidden_layers: [256, 128, 64]
|
|
108
108
|
learning_rate: 0.0008
|
|
109
109
|
epochs: 50
|
|
110
110
|
|
|
111
111
|
- name: "xgb_training_a"
|
|
112
|
-
|
|
112
|
+
code: "define_xgb_training_process"
|
|
113
113
|
parameters:
|
|
114
114
|
n_estimators: 350
|
|
115
115
|
max_depth: 4
|
|
@@ -118,7 +118,7 @@ experiment:
|
|
|
118
118
|
colsample_bytree: 0.9
|
|
119
119
|
|
|
120
120
|
- name: "xgb_training_b"
|
|
121
|
-
|
|
121
|
+
code: "define_xgb_training_process"
|
|
122
122
|
parameters:
|
|
123
123
|
n_estimators: 500
|
|
124
124
|
max_depth: 8
|
|
@@ -127,22 +127,22 @@ experiment:
|
|
|
127
127
|
colsample_bytree: 0.8
|
|
128
128
|
|
|
129
129
|
- name: "nn_inference_a"
|
|
130
|
-
|
|
130
|
+
code: "define_nn_inference_process"
|
|
131
131
|
parameters:
|
|
132
132
|
train_key: nn_training_a
|
|
133
133
|
|
|
134
134
|
- name: "nn_inference_b"
|
|
135
|
-
|
|
135
|
+
code: "define_nn_inference_process"
|
|
136
136
|
parameters:
|
|
137
137
|
train_key: nn_training_b
|
|
138
138
|
|
|
139
139
|
- name: "xgb_inference_a"
|
|
140
|
-
|
|
140
|
+
code: "define_xgb_inference_process"
|
|
141
141
|
parameters:
|
|
142
142
|
train_key: xgb_training_a
|
|
143
143
|
|
|
144
144
|
- name: "xgb_inference_b"
|
|
145
|
-
|
|
145
|
+
code: "define_xgb_inference_process"
|
|
146
146
|
parameters:
|
|
147
147
|
train_key: xgb_training_b
|
|
148
148
|
|
|
@@ -160,31 +160,31 @@ experiment:
|
|
|
160
160
|
|
|
161
161
|
- name: "pca_scree"
|
|
162
162
|
description: "Render PCA scree plot"
|
|
163
|
-
|
|
163
|
+
code: "reporting.pca_scree"
|
|
164
164
|
environment: "premier-league-env-reporting"
|
|
165
165
|
probe_paths:
|
|
166
|
-
feat: "//*[@name
|
|
166
|
+
feat: "//*[@name='feature_analysis']"
|
|
167
167
|
|
|
168
168
|
- name: "goals_distribution"
|
|
169
|
-
|
|
169
|
+
code: "reporting.goals_distribution"
|
|
170
170
|
environment: "premier-league-env-reporting"
|
|
171
171
|
probe_paths:
|
|
172
|
-
feat: "//*[@name
|
|
172
|
+
feat: "//*[@name='derive_labels_and_indices']"
|
|
173
173
|
|
|
174
174
|
- name: "test_metrics_comparison"
|
|
175
|
-
|
|
175
|
+
code: "reporting.test_metrics_comparison"
|
|
176
176
|
environment: "premier-league-env-reporting"
|
|
177
177
|
probe_paths:
|
|
178
|
-
linear: "//*[@
|
|
179
|
-
nn_best: "//*[@
|
|
180
|
-
xgb_best: "//*[@
|
|
181
|
-
ensemble: "//*[@
|
|
178
|
+
linear: "//*[@name='linear_inference']/*[@name='test_inference_classification']"
|
|
179
|
+
nn_best: "//*[@name='nn_best_inference']/*[@name='test_inference_classification']"
|
|
180
|
+
xgb_best: "//*[@name='xgb_best_inference']/*[@name='test_inference_classification']"
|
|
181
|
+
ensemble: "//*[@name='ensemble_inference']"
|
|
182
182
|
|
|
183
183
|
- name: "nn_losses"
|
|
184
|
-
|
|
184
|
+
code: "reporting_js.nn_losses"
|
|
185
185
|
environment: "premier-league-env-reporting"
|
|
186
186
|
chart_type: "dynamic"
|
|
187
187
|
probe_paths:
|
|
188
|
-
nn_a_p1_seed41: "//*[@partition
|
|
189
|
-
nn_a_p2_seed41: "//*[@partition
|
|
190
|
-
nn_a_p1_seed42: "//*[@partition
|
|
188
|
+
nn_a_p1_seed41: "//*[@partition='p1']/*[@seed='41']/*[@name='nn_training_a']/*[@name='train_and_evaluate_nn_classifier']"
|
|
189
|
+
nn_a_p2_seed41: "//*[@partition='p2']/*[@seed='41']/*[@name='nn_training_a']/*[@name='train_and_evaluate_nn_classifier']"
|
|
190
|
+
nn_a_p1_seed42: "//*[@partition='p1']/*[@seed='42']/*[@name='nn_training_a']/*[@name='train_and_evaluate_nn_classifier']"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"service_account","project_id":"mlops-platform-470017","private_key_id":"0a5f97bd39814e45553c7ddca399e504c4821f04","private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBDxZkEB6KTMyS\n4/KKOHlbzwTLFTAXYWyKY15X6hhetwrRzAMWoGX1/fopvkf3u60gevrJq4R+UOTe\nZ1XGhySrpAyEtr2KPD9Zz0H+EgdLlBM+SZHfKkrM3Iln08LV501EF+miouaZ+Pn4\naNBYcQH5ry9BAADfysHj9fS92OVvGQ1cfj/RMUVOA9wHTBoFUzzwDVyGgUv6oA6K\nJLWzx4Z9Uh5W1PpkBVxaFrvzGMIQ/YOlgN6Ii67mR3RvYdXsEgNoRudiiG0djNDP\n/NnRm3D8Eo93Kxv5K9dJeKGX3lobSGfGPvqe6FqeUOlTDb8PS3LbTsQj3vW/EZ8Y\nsOcGnSmxAgMBAAECggEADUDO5bGhlxbsmEzjso9r7VyJ1LMwqPo8LEfkCx7760yf\nhoV91MjQFxIHYWQ5po0QtoEaxEts/grpm0b2wXk3kDzjyxyMaHf4VV8ABcQREiQp\nKgZ86BgMKIIfQKxt3KdTS5BkrpCGSh2oc2AmpVukqhJXqRup87MEmnShB4NjRLwR\n80jaYHOsTul4fDxcofzgUWTd+wou6fTWNrPPGDP3bvGUD2ktuXwmJ1Ye8wXfuiaW\nVgTa5dNsY/DoRqfXe9QagJ9cdmm0SjQppAxY8+FcHheSuwzp7XWAUegzc8rCqQp5\nlKW47CL6UdTN7jx2AEadannZiYgN4A51yu0bX0vd0QKBgQDtPTj3OSG61LxJSycb\n6SnoO+2jBskDsbnWFSaPj88GFiitLPegH9kr54uoea5mOtpNSt7mW3iTy/V3zABc\nf+KK/5l6ZpV+CVaVndd6LRBt3aFBbEx3MZGcPeIoZDEqYTkROQlJKBRYrDpHJAAe\nQhbHl0XP+4EmbMTqNPL4kD9f/QKBgQDQU3XlkWaABwispE+7r58K8Z+7Uzapzlj7\nM0UsdLXj/FtmwEx5PWiVFCDEiy3F17aAsJmcEhXEl3yYARdES5q1/fAcBrGguxQK\n0Uzc+958oodV+SL70V147k+NvmPtrOhwYMCdRavAk5/zUrVNrvS6rk/aMFscwheR\ns9YnkY08xQKBgFHUFSsI1L6JlCw/tMymvuKwkQXzWDBjA/N4tiU8eC7kQ8F/yiW7\n9n9zTTOeTO5MDHIuifWwxUVNS2y3u8QsBCrngLu8WIHxnKDcoa8tjgYnNPDI9xMB\n0PSwT6qY7HBuum1bW/2UynfPaAaTv0qwhwTvA5B6cpCbYcxa2RetQX7NAoGBALm3\n/d23vvIZ0zDdlrtSeG2wNF8ycwP0kjz3Ww6jyVfDLNSwUDyxcx+b/w5YqWi7wtZ9\nGaQaN0+4YPm91zkqUXU3OiwyB36r6NUv+97xp0njNtV9MiPeZP8fLMrHVZyYTeuJ\nRY2vg8s9kBXCwSDM/R3Dsipoe4FqwspA0YUa7BelAoGAOOLY6E7sdE76Tuzd7jxh\nWrO1vY0H6PO3SWns14J3/ZcpXPda2RvpResoDLQ2BDSdWO8oRcWkv8IctODkKLkb\nCCf+n82KnZmaAd9mBW9mdX+1djkBiEjYBW6XPAP3QUZLsf+jxTlIu/20X+iUo/LC\nlVoi6u3ZyJbX90OIkIWkOiA=\n-----END PRIVATE KEY-----\n","client_email":"platform@mlops-platform-470017.iam.gserviceaccount.com","client_id":"102504406075317026901","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/platform%40mlops-platform-470017.iam.gserviceaccount.com","universe_domain":"googleapis.com"}
|
|
@@ -16,13 +16,21 @@ docs/features/reporting.md
|
|
|
16
16
|
docs/features/seed-parallelism.md
|
|
17
17
|
docs/getting-started/creating-a-project.md
|
|
18
18
|
docs/getting-started/quick-start.md
|
|
19
|
-
docs/project-structure/charts.md
|
|
20
19
|
docs/project-structure/configuration.md
|
|
21
20
|
docs/project-structure/model-code.md
|
|
22
21
|
docs/project-structure/overview.md
|
|
23
22
|
docs/templates/premier-league.md
|
|
24
23
|
docs/templates/sklearn-basic.md
|
|
25
24
|
docs/web-ui/local-ui.md
|
|
25
|
+
premier-league/firestore.json
|
|
26
|
+
premier-league/requirements.txt
|
|
27
|
+
premier-league/charts/plot_metrics.js
|
|
28
|
+
premier-league/charts/plot_metrics.py
|
|
29
|
+
premier-league/charts/requirements.txt
|
|
30
|
+
premier-league/configs/compute_config.yaml
|
|
31
|
+
premier-league/configs/project_config.yaml
|
|
32
|
+
premier-league/data/England CSV.csv
|
|
33
|
+
premier-league/models/premier_league_model.py
|
|
26
34
|
src/__init__.py
|
|
27
35
|
src/expops/__init__.py
|
|
28
36
|
src/expops/__main__.py
|
|
@@ -103,14 +111,6 @@ src/mlops/storage/adapters/redis_store.py
|
|
|
103
111
|
src/mlops/storage/adapters/sqlite_store.py
|
|
104
112
|
src/mlops/storage/interfaces/__init__.py
|
|
105
113
|
src/mlops/storage/interfaces/kv_store.py
|
|
106
|
-
src/mlops/templates/premier-league/requirements.txt
|
|
107
|
-
src/mlops/templates/premier-league/charts/plot_metrics.js
|
|
108
|
-
src/mlops/templates/premier-league/charts/plot_metrics.py
|
|
109
|
-
src/mlops/templates/premier-league/charts/requirements.txt
|
|
110
|
-
src/mlops/templates/premier-league/configs/compute_config.yaml
|
|
111
|
-
src/mlops/templates/premier-league/configs/project_config.yaml
|
|
112
|
-
src/mlops/templates/premier-league/data/England CSV.csv
|
|
113
|
-
src/mlops/templates/premier-league/models/premier_league_model.py
|
|
114
114
|
src/mlops/templates/sklearn-basic/README.md
|
|
115
115
|
src/mlops/templates/sklearn-basic/requirements.txt
|
|
116
116
|
src/mlops/templates/sklearn-basic/charts/plot_metrics.py
|
|
@@ -134,13 +134,19 @@ tests/unit/test_core/test_data_hashing.py
|
|
|
134
134
|
tests/unit/test_core/test_executor_worker.py
|
|
135
135
|
tests/unit/test_core/test_graph_expansion.py
|
|
136
136
|
tests/unit/test_core/test_networkx_parser.py
|
|
137
|
+
tests/unit/test_core/test_networkx_parser_code_field.py
|
|
137
138
|
tests/unit/test_core/test_payload_spill.py
|
|
139
|
+
tests/unit/test_core/test_pipeline_tree_steps.py
|
|
138
140
|
tests/unit/test_core/test_prepare_runner_kwargs.py
|
|
141
|
+
tests/unit/test_core/test_probe_path_selectors_xpath.py
|
|
139
142
|
tests/unit/test_core/test_process_hashing.py
|
|
140
143
|
tests/unit/test_core/test_step_state_manager.py
|
|
141
144
|
tests/unit/test_core/test_step_system.py
|
|
142
145
|
tests/unit/test_managers/__init__.py
|
|
143
146
|
tests/unit/test_managers/test_reproducibility_manager.py
|
|
147
|
+
tests/unit/test_platform/test_dynamic_js_charts.py
|
|
148
|
+
tests/unit/test_platform/test_project_metadata_resilience.py
|
|
149
|
+
tests/unit/test_reporting/test_entrypoint.py
|
|
144
150
|
tests/unit/test_storage/__init__.py
|
|
145
151
|
tests/unit/test_storage/test_factory.py
|
|
146
152
|
tests/unit/test_storage/test_gcp_kv_store.py
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.17.dev0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 17, 'dev0')
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g8934518e8'
|
|
@@ -181,20 +181,34 @@ class CustomModelAdapter(ModelAdapter):
|
|
|
181
181
|
probe_paths = proc.get("probe_paths")
|
|
182
182
|
chart_type = proc.get("chart_type")
|
|
183
183
|
is_chart = (proc_type == "chart") or (probe_paths is not None) or (chart_type is not None)
|
|
184
|
+
|
|
185
|
+
# Derive script key from unified `code` field where possible.
|
|
186
|
+
raw_code = proc.get("code")
|
|
187
|
+
code_str = str(raw_code).strip() if isinstance(raw_code, str) else ""
|
|
188
|
+
has_explicit_code = bool(code_str)
|
|
189
|
+
has_data_parallelism = proc.get("data_parallelism") is not None
|
|
190
|
+
has_seed_parallelism = proc.get("seed_parallelism") is not None
|
|
191
|
+
is_default_split = (not has_explicit_code) and (has_data_parallelism or has_seed_parallelism)
|
|
192
|
+
|
|
193
|
+
script_key: str | None = None
|
|
194
|
+
if has_explicit_code:
|
|
195
|
+
if "." in code_str:
|
|
196
|
+
script_key, _ = code_str.rsplit(".", 1)
|
|
197
|
+
script_key = script_key or None
|
|
198
|
+
else:
|
|
199
|
+
# No explicit script key: use default script
|
|
200
|
+
script_key = None
|
|
201
|
+
|
|
184
202
|
if is_chart:
|
|
185
|
-
script_key = proc.get("script")
|
|
186
203
|
script_path = resolve_script_path(script_key, scripts_section, default_script) if scripts_section else None
|
|
187
204
|
if script_path and str(script_path).lower().endswith(".py"):
|
|
188
205
|
chart_script_paths.append(str(script_path))
|
|
189
206
|
continue
|
|
190
|
-
|
|
207
|
+
|
|
191
208
|
script_path = resolve_script_path(script_key, scripts_section, default_script) if scripts_section else None
|
|
192
|
-
has_code_function = bool(proc.get("code_function"))
|
|
193
|
-
has_data_parallelism = proc.get("data_parallelism") is not None
|
|
194
|
-
has_seed_parallelism = proc.get("seed_parallelism") is not None
|
|
195
|
-
is_default_split = (not has_code_function) and (has_data_parallelism or has_seed_parallelism)
|
|
196
209
|
if not script_path:
|
|
197
210
|
if is_default_split:
|
|
211
|
+
# Default data/seed split nodes without explicit code may omit scripts.
|
|
198
212
|
continue
|
|
199
213
|
missing_script_paths.append(str(proc.get("name", "unknown")))
|
|
200
214
|
continue
|
|
@@ -204,7 +218,7 @@ class CustomModelAdapter(ModelAdapter):
|
|
|
204
218
|
missing = ", ".join(sorted(set(missing_script_paths)))
|
|
205
219
|
raise ValueError(
|
|
206
220
|
"script must be specified for non-chart processes "
|
|
207
|
-
"(except default data/seed split nodes without
|
|
221
|
+
"(except default data/seed split nodes without explicit code): "
|
|
208
222
|
f"{missing}"
|
|
209
223
|
)
|
|
210
224
|
if not script_paths:
|
|
@@ -563,15 +577,16 @@ class CustomModelAdapter(ModelAdapter):
|
|
|
563
577
|
proc_type = str(proc.get("type", "process"))
|
|
564
578
|
if proc_type == "chart":
|
|
565
579
|
continue
|
|
566
|
-
|
|
567
|
-
|
|
580
|
+
raw_code = proc.get("code")
|
|
581
|
+
code_str = str(raw_code).strip() if isinstance(raw_code, str) else ""
|
|
582
|
+
has_explicit_code = bool(code_str)
|
|
568
583
|
has_data_parallelism = proc.get("data_parallelism") is not None
|
|
569
584
|
has_seed_parallelism = proc.get("seed_parallelism") is not None
|
|
570
|
-
is_default_split = (not
|
|
585
|
+
is_default_split = (not has_explicit_code) and (has_data_parallelism or has_seed_parallelism)
|
|
571
586
|
if is_default_split:
|
|
572
587
|
continue
|
|
573
|
-
# For non-split processes,
|
|
574
|
-
#
|
|
588
|
+
# For non-split processes, we require either explicit code or default
|
|
589
|
+
# behavior (code omitted but no split flags). In both cases validation passes.
|
|
575
590
|
return True
|
|
576
591
|
except Exception:
|
|
577
592
|
return False
|