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.
Files changed (159) hide show
  1. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/PKG-INFO +1 -1
  2. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/data-parallelism.md +9 -10
  3. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/pipelines.md +7 -5
  4. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/reporting.md +9 -32
  5. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/seed-parallelism.md +15 -14
  6. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/configuration.md +8 -3
  7. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/overview.md +0 -2
  8. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.js +18 -11
  9. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/plot_metrics.py +7 -0
  10. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/configs/project_config.yaml +21 -21
  11. expops-0.1.17.dev0/premier-league/firestore.json +1 -0
  12. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/PKG-INFO +1 -1
  13. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/SOURCES.txt +15 -9
  14. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/_version.py +3 -3
  15. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/custom/custom_adapter.py +27 -12
  16. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/executor_worker.py +127 -15
  17. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/networkx_parser.py +35 -11
  18. expops-0.1.17.dev0/src/mlops/core/pipeline_tree.py +524 -0
  19. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/pipeline_utils.py +34 -5
  20. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/probe_path_selectors.py +6 -1
  21. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/process_hashing.py +19 -5
  22. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/step_state_manager.py +80 -17
  23. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/step_system.py +3 -5
  24. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/managers/project_manager.py +91 -11
  25. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/platform.py +274 -12
  26. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/entrypoint.py +61 -2
  27. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/factory.py +26 -12
  28. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/configs/project_config.yaml +3 -3
  29. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/server.py +46 -13
  30. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/mlops-charts.js +64 -13
  31. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/script.js +124 -26
  32. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/testing-plan.md +17 -7
  33. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_executor_worker.py +72 -0
  34. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_graph_expansion.py +29 -0
  35. expops-0.1.17.dev0/tests/unit/test_core/test_networkx_parser_code_field.py +78 -0
  36. expops-0.1.17.dev0/tests/unit/test_core/test_pipeline_tree_steps.py +101 -0
  37. expops-0.1.17.dev0/tests/unit/test_core/test_probe_path_selectors_xpath.py +81 -0
  38. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_process_hashing.py +1 -1
  39. expops-0.1.17.dev0/tests/unit/test_core/test_step_state_manager.py +90 -0
  40. expops-0.1.17.dev0/tests/unit/test_platform/test_dynamic_js_charts.py +146 -0
  41. expops-0.1.17.dev0/tests/unit/test_platform/test_project_metadata_resilience.py +94 -0
  42. expops-0.1.17.dev0/tests/unit/test_reporting/test_entrypoint.py +53 -0
  43. expops-0.1.16.dev0/docs/project-structure/charts.md +0 -160
  44. expops-0.1.16.dev0/src/mlops/core/pipeline_tree.py +0 -234
  45. expops-0.1.16.dev0/tests/unit/test_core/test_step_state_manager.py +0 -49
  46. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.github/workflows/ci.yml +0 -0
  47. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.github/workflows/release.yml +0 -0
  48. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/.gitignore +0 -0
  49. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/LICENSE +0 -0
  50. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/README.md +0 -0
  51. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/advanced/backends.md +0 -0
  52. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/caching.md +0 -0
  53. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/distributed.md +0 -0
  54. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/features/environments.md +0 -0
  55. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/getting-started/creating-a-project.md +0 -0
  56. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/getting-started/quick-start.md +0 -0
  57. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/index.md +0 -0
  58. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/project-structure/model-code.md +0 -0
  59. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/requirements.txt +0 -0
  60. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/templates/premier-league.md +0 -0
  61. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/templates/sklearn-basic.md +0 -0
  62. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/docs/web-ui/local-ui.md +0 -0
  63. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/charts/requirements.txt +0 -0
  64. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/configs/compute_config.yaml +0 -0
  65. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/data/England CSV.csv +0 -0
  66. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/models/premier_league_model.py +0 -0
  67. {expops-0.1.16.dev0/src/mlops/templates → expops-0.1.17.dev0}/premier-league/requirements.txt +0 -0
  68. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/pyproject.toml +0 -0
  69. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/setup.cfg +0 -0
  70. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/__init__.py +0 -0
  71. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/__init__.py +0 -0
  72. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/__main__.py +0 -0
  73. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/core/__init__.py +0 -0
  74. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/main.py +0 -0
  75. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/__init__.py +0 -0
  76. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/context.py +0 -0
  77. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/reporting/registry.py +0 -0
  78. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/web/__init__.py +0 -0
  79. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops/web/server.py +0 -0
  80. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/dependency_links.txt +0 -0
  81. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/entry_points.txt +0 -0
  82. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/requires.txt +0 -0
  83. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/expops.egg-info/top_level.txt +0 -0
  84. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/__init__.py +0 -0
  85. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/__main__.py +0 -0
  86. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/__init__.py +0 -0
  87. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/base.py +0 -0
  88. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/config_schema.py +0 -0
  89. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/custom/__init__.py +0 -0
  90. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/plugin_manager.py +0 -0
  91. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/sklearn/__init__.py +0 -0
  92. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/adapters/sklearn/adapter.py +0 -0
  93. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/__init__.py +0 -0
  94. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/controller.py +0 -0
  95. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/process_runner.py +0 -0
  96. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/cluster/providers.py +0 -0
  97. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/__init__.py +0 -0
  98. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/compute_config.py +0 -0
  99. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/custom_model_base.py +0 -0
  100. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/dask_networkx_executor.py +0 -0
  101. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/data_hashing.py +0 -0
  102. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/graph_expansion.py +0 -0
  103. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/graph_types.py +0 -0
  104. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/payload_spill.py +0 -0
  105. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/core/workspace.py +0 -0
  106. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/__init__.py +0 -0
  107. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/base.py +0 -0
  108. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/conda_manager.py +0 -0
  109. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/factory.py +0 -0
  110. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/pyenv_manager.py +0 -0
  111. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/setup_env.py +0 -0
  112. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/system_manager.py +0 -0
  113. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/utils.py +0 -0
  114. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/environment/venv_manager.py +0 -0
  115. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/main.py +0 -0
  116. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/managers/reproducibility_manager.py +0 -0
  117. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/__init__.py +0 -0
  118. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/context.py +0 -0
  119. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/kv_utils.py +0 -0
  120. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/reporting/registry.py +0 -0
  121. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/__init__.py +0 -0
  122. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/context.py +0 -0
  123. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/runtime/env_export.py +0 -0
  124. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/__init__.py +0 -0
  125. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/__init__.py +0 -0
  126. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/gcp_kv_store.py +0 -0
  127. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/gcs_object_store.py +0 -0
  128. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/memory_store.py +0 -0
  129. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/redis_store.py +0 -0
  130. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/adapters/sqlite_store.py +0 -0
  131. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/interfaces/__init__.py +0 -0
  132. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/interfaces/kv_store.py +0 -0
  133. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/storage/path_utils.py +0 -0
  134. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/README.md +0 -0
  135. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/charts/plot_metrics.py +0 -0
  136. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/charts/requirements.txt +0 -0
  137. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/configs/compute_config.yaml +0 -0
  138. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/data/train.csv +0 -0
  139. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/models/model.py +0 -0
  140. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/templates/sklearn-basic/requirements.txt +0 -0
  141. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/__init__.py +0 -0
  142. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/index.html +0 -0
  143. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/src/mlops/web/ui/styles.css +0 -0
  144. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/conftest.py +0 -0
  145. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/__init__.py +0 -0
  146. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/__init__.py +0 -0
  147. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_dask_networkx_executor.py +0 -0
  148. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_data_hashing.py +0 -0
  149. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_networkx_parser.py +0 -0
  150. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_payload_spill.py +0 -0
  151. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_prepare_runner_kwargs.py +0 -0
  152. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_core/test_step_system.py +0 -0
  153. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_managers/__init__.py +0 -0
  154. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_managers/test_reproducibility_manager.py +0 -0
  155. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/__init__.py +0 -0
  156. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_factory.py +0 -0
  157. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_gcp_kv_store.py +0 -0
  158. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_gcs_object_store.py +0 -0
  159. {expops-0.1.16.dev0 → expops-0.1.17.dev0}/tests/unit/test_storage/test_sqlite_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: expops
3
- Version: 0.1.16.dev0
3
+ Version: 0.1.17.dev0
4
4
  Summary: MLOps Platform with step-based pipeline execution
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -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 `code_function`)
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 `code_function`; it will merge its upstream outputs and split the `data_name` key.
21
- - `script` is optional for split nodes with no `code_function`.
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 (`code_function`)
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
- code_function: "define_nn_data_parallel"
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 `code_function`.
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
- code_function: "define_aggregate_results"
60
+ code: "define_aggregate_results"
61
61
  ```
62
62
 
63
- Aggregation processes **must** define a `code_function`.
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
- code_function: "define_feature_engineering_process"
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
- code_function: "define_training_process"
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
- - `script` (optional): Key from the top-level `scripts` section to use for this process. Defaults to the first key in `scripts` if omitted. See [Configuration](../project-structure/configuration.md) for the `scripts` section and defaults.
71
- - `code_function`: Name of the Python function that defines the process (see below). Defaults to the process name if omitted, so you can omit it when the function name matches the process name.
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 must match the `code_function` in the config:
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=\"train_model\"]"
73
- eval: "//*[@name=\"evaluate_model\"]"
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="process_name"]` |
104
- | Process + step | `//*[@name="process_name"]/step[@name="step_name"]` or `//*[@name="process_name"]/*[@name="step_name"]` |
105
- | Specific partition/seed | `//*[@partition="p1"]/*[@seed="41"]/*[@name="process_name"]` |
106
- | Any partition/seed | `//*[@partition]/*[@seed]/*[@name="process_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. `nn_training_a__p1_seed41/train_and_evaluate_nn_classifier`). Chart code can iterate over keys or use prefix/grouping logic to aggregate across partitions or seeds.
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
- - `script` - a key that resolves to your JS chart script (e.g. `reporting_js`), defined under `scripts:` at the top of the config
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
- script: "reporting_js"
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
- # Seed Parallelism
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 `code_function`)
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 `code_function`; it will merge upstream outputs and pass them through.
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 use a `_seed<value>` suffix (for example, `train_model_seed41`).
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 (`code_function`)
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
- code_function: "define_seed_parallel_process"
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
- code_function: "define_aggregate_seeds"
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 parallelism duplicates downstream nodes and is visible in the UI.
78
- - Task-level RNG seeding uses the most recent (innermost) seed layer when multiple
79
- seed-parallel layers are nested.
80
- - Each aggregation collapses only the most recent seed-parallel layer; outer layers
81
- remain as separate process nodes.
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
- - **Script**: If a process does not set `script`, the **first key** in the top-level `scripts` section is used. List your main script first so most processes need no `script` field.
45
- - **code_function**: If omitted, it defaults to the process **name**. When your Python function name matches the process name (e.g. process `train_model` and function `train_model`), you can omit `code_function`.
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) and [Chart Generation](charts.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
@@ -17,16 +17,22 @@ chart('nn_losses', (probePaths, ctx, listener) => {
17
17
  datasets: []
18
18
  };
19
19
 
20
- const formatProbeLabel = (probePath) => {
21
- const raw = String(probePath || '');
22
- const proc = raw.split('/')[0] || raw;
23
- const partMatch = proc.match(/__p(\d+)/) || proc.match(/_p(\d+)/);
24
- const seedMatch = proc.match(/_seed(\d+)/);
25
- const base = proc.replace(/__p\d+/, '').replace(/_p\d+/, '').replace(/_seed\d+/, '');
26
- const parts = [base];
27
- if (partMatch) parts.push(`P${partMatch[1]}`);
28
- if (seedMatch) parts.push(`S${seedMatch[1]}`);
29
- return parts.join(' ');
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(k).replace(/_/g, ' ').toUpperCase(),
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',
@@ -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
- code_function: "define_nn_training_process"
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
- code_function: "define_nn_training_process"
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
- code_function: "define_xgb_training_process"
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
- code_function: "define_xgb_training_process"
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
- code_function: "define_nn_inference_process"
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
- code_function: "define_nn_inference_process"
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
- code_function: "define_xgb_inference_process"
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
- code_function: "define_xgb_inference_process"
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
- script: "reporting"
163
+ code: "reporting.pca_scree"
164
164
  environment: "premier-league-env-reporting"
165
165
  probe_paths:
166
- feat: "//*[@name=\"feature_engineering_generic\"]/step[@name=\"feature_analysis\"]"
166
+ feat: "//*[@name='feature_analysis']"
167
167
 
168
168
  - name: "goals_distribution"
169
- script: "reporting"
169
+ code: "reporting.goals_distribution"
170
170
  environment: "premier-league-env-reporting"
171
171
  probe_paths:
172
- feat: "//*[@name=\"feature_engineering_generic\"]/step[@name=\"derive_labels_and_indices\"]"
172
+ feat: "//*[@name='derive_labels_and_indices']"
173
173
 
174
174
  - name: "test_metrics_comparison"
175
- script: "reporting"
175
+ code: "reporting.test_metrics_comparison"
176
176
  environment: "premier-league-env-reporting"
177
177
  probe_paths:
178
- linear: "//*[@partition]/*[@seed]/*[@name=\"linear_inference\"]/*[@name=\"test_inference_classification\"]"
179
- nn_best: "//*[@partition]/*[@seed]/*[@name=\"nn_best_inference\"]/step[@name=\"test_inference_classification\"]"
180
- xgb_best: "//*[@partition]/*[@seed]/*[@name=\"xgb_best_inference\"]/step[@name=\"test_inference_classification\"]"
181
- ensemble: "//*[@partition]/*[@seed]/*[@name=\"ensemble_inference\"]"
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
- script: "reporting_js"
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=\"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\"]"
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"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: expops
3
- Version: 0.1.16.dev0
3
+ Version: 0.1.17.dev0
4
4
  Summary: MLOps Platform with step-based pipeline execution
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -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.16.dev0'
32
- __version_tuple__ = version_tuple = (0, 1, 16, 'dev0')
31
+ __version__ = version = '0.1.17.dev0'
32
+ __version_tuple__ = version_tuple = (0, 1, 17, 'dev0')
33
33
 
34
- __commit_id__ = commit_id = 'ga8d9fd60b'
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
- script_key = proc.get("script")
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 code_function): "
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
- # script is optional - default script will be used if omitted
567
- has_code_function = bool(proc.get("code_function"))
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 has_code_function) and (has_data_parallelism or has_seed_parallelism)
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, script key is optional (defaults to first script)
574
- # Validation passes if script key exists or will use default
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