pipewright-mlops 1.0.0__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 (221) hide show
  1. pipewright_mlops-1.0.0/.dockerignore +5 -0
  2. pipewright_mlops-1.0.0/.gitattributes +6 -0
  3. pipewright_mlops-1.0.0/.github/workflows/ci.yml +64 -0
  4. pipewright_mlops-1.0.0/.github/workflows/release.yml +132 -0
  5. pipewright_mlops-1.0.0/.gitignore +36 -0
  6. pipewright_mlops-1.0.0/CHANGELOG.md +128 -0
  7. pipewright_mlops-1.0.0/CONTEXT.md +1052 -0
  8. pipewright_mlops-1.0.0/CONTRIBUTING.md +52 -0
  9. pipewright_mlops-1.0.0/LICENSE +202 -0
  10. pipewright_mlops-1.0.0/NOTICE +7 -0
  11. pipewright_mlops-1.0.0/PKG-INFO +549 -0
  12. pipewright_mlops-1.0.0/README.md +510 -0
  13. pipewright_mlops-1.0.0/SECURITY.md +24 -0
  14. pipewright_mlops-1.0.0/STABILITY.md +41 -0
  15. pipewright_mlops-1.0.0/docs/FEATURES.md +126 -0
  16. pipewright_mlops-1.0.0/docs/ONBOARDING.md +149 -0
  17. pipewright_mlops-1.0.0/docs/images/pipewright-architecture.drawio +1 -0
  18. pipewright_mlops-1.0.0/docs/images/pipewright-architecture.png +0 -0
  19. pipewright_mlops-1.0.0/docs/images/pipewright-gke-architecture.drawio +1 -0
  20. pipewright_mlops-1.0.0/docs/images/pipewright-gke-architecture.png +0 -0
  21. pipewright_mlops-1.0.0/examples/churn/components/champion.py +51 -0
  22. pipewright_mlops-1.0.0/examples/churn/components/churn_net.py +83 -0
  23. pipewright_mlops-1.0.0/examples/churn/components/evaluate.py +90 -0
  24. pipewright_mlops-1.0.0/examples/churn/components/ingest.py +61 -0
  25. pipewright_mlops-1.0.0/examples/churn/components/mlkit.py +369 -0
  26. pipewright_mlops-1.0.0/examples/churn/components/monitor.py +216 -0
  27. pipewright_mlops-1.0.0/examples/churn/components/prepare.py +50 -0
  28. pipewright_mlops-1.0.0/examples/churn/components/record.py +51 -0
  29. pipewright_mlops-1.0.0/examples/churn/components/register.py +114 -0
  30. pipewright_mlops-1.0.0/examples/churn/components/requirements.txt +19 -0
  31. pipewright_mlops-1.0.0/examples/churn/components/score.py +63 -0
  32. pipewright_mlops-1.0.0/examples/churn/components/torchserve_handler.py +63 -0
  33. pipewright_mlops-1.0.0/examples/churn/components/train.py +80 -0
  34. pipewright_mlops-1.0.0/examples/churn/components/tune.py +75 -0
  35. pipewright_mlops-1.0.0/examples/churn/components/validate.py +40 -0
  36. pipewright_mlops-1.0.0/examples/churn/config/data.yaml +32 -0
  37. pipewright_mlops-1.0.0/examples/churn/config/evaluation.yaml +31 -0
  38. pipewright_mlops-1.0.0/examples/churn/config/operations.yaml.tmpl +33 -0
  39. pipewright_mlops-1.0.0/examples/churn/config/runtime.yaml.tmpl +14 -0
  40. pipewright_mlops-1.0.0/examples/churn/config/synthetic.yaml +22 -0
  41. pipewright_mlops-1.0.0/examples/churn/config/tables.yaml.tmpl +28 -0
  42. pipewright_mlops-1.0.0/examples/churn/config/training.yaml +21 -0
  43. pipewright_mlops-1.0.0/examples/churn/config/triggers.yaml.tmpl +10 -0
  44. pipewright_mlops-1.0.0/examples/churn/data/generate_sample.py +85 -0
  45. pipewright_mlops-1.0.0/examples/churn/data/sample_customers.csv +5001 -0
  46. pipewright_mlops-1.0.0/examples/churn/data/scoring_customers.csv +1001 -0
  47. pipewright_mlops-1.0.0/examples/churn/data/scoring_drifted.csv +1001 -0
  48. pipewright_mlops-1.0.0/examples/churn/environments/dev.yaml +8 -0
  49. pipewright_mlops-1.0.0/examples/churn/inference_pipeline.py +61 -0
  50. pipewright_mlops-1.0.0/examples/churn/schemas/churn_outcomes.json +17 -0
  51. pipewright_mlops-1.0.0/examples/churn/schemas/model_metrics.json +127 -0
  52. pipewright_mlops-1.0.0/examples/churn/schemas/model_monitoring.json +82 -0
  53. pipewright_mlops-1.0.0/examples/churn/schemas/predictions.json +10 -0
  54. pipewright_mlops-1.0.0/examples/churn/schemas/raw_customers.json +72 -0
  55. pipewright_mlops-1.0.0/examples/churn/tests/requirements.txt +4 -0
  56. pipewright_mlops-1.0.0/examples/churn/tests/test_mlkit.py +149 -0
  57. pipewright_mlops-1.0.0/examples/churn/training_pipeline.py +93 -0
  58. pipewright_mlops-1.0.0/examples/churn/usecase.yaml.tmpl +41 -0
  59. pipewright_mlops-1.0.0/examples/features/config/data.yaml.tmpl +33 -0
  60. pipewright_mlops-1.0.0/examples/features/data/customer_profile.csv +15001 -0
  61. pipewright_mlops-1.0.0/examples/features/data/entities.csv +5001 -0
  62. pipewright_mlops-1.0.0/examples/features/data/load_tables.sh +21 -0
  63. pipewright_mlops-1.0.0/examples/features/data/support_activity.csv +10001 -0
  64. pipewright_mlops-1.0.0/examples/features/example.yaml +12 -0
  65. pipewright_mlops-1.0.0/examples/features/usecase.yaml.tmpl +49 -0
  66. pipewright_mlops-1.0.0/examples/minimal/config/data.yaml +21 -0
  67. pipewright_mlops-1.0.0/examples/minimal/example.yaml +9 -0
  68. pipewright_mlops-1.0.0/examples/minimal/usecase.yaml.tmpl +39 -0
  69. pipewright_mlops-1.0.0/examples/ray/components/ray_tune.py +56 -0
  70. pipewright_mlops-1.0.0/examples/ray/components/ray_tune_job.py +66 -0
  71. pipewright_mlops-1.0.0/examples/ray/components/requirements.txt +22 -0
  72. pipewright_mlops-1.0.0/examples/ray/config/data.yaml +21 -0
  73. pipewright_mlops-1.0.0/examples/ray/example.yaml +9 -0
  74. pipewright_mlops-1.0.0/examples/ray/ray-requirements.txt +5 -0
  75. pipewright_mlops-1.0.0/examples/ray/training_pipeline.py +84 -0
  76. pipewright_mlops-1.0.0/examples/ray/usecase.yaml.tmpl +57 -0
  77. pipewright_mlops-1.0.0/images/launcher.Dockerfile +35 -0
  78. pipewright_mlops-1.0.0/pyproject.toml +77 -0
  79. pipewright_mlops-1.0.0/scripts/check_package.py +64 -0
  80. pipewright_mlops-1.0.0/scripts/smoke_test.sh +40 -0
  81. pipewright_mlops-1.0.0/src/pipewright/__init__.py +10 -0
  82. pipewright_mlops-1.0.0/src/pipewright/adapters/__init__.py +23 -0
  83. pipewright_mlops-1.0.0/src/pipewright/adapters/kfp.py +149 -0
  84. pipewright_mlops-1.0.0/src/pipewright/adapters/vertex.py +65 -0
  85. pipewright_mlops-1.0.0/src/pipewright/alerts/__init__.py +0 -0
  86. pipewright_mlops-1.0.0/src/pipewright/alerts/sync.py +246 -0
  87. pipewright_mlops-1.0.0/src/pipewright/build/__init__.py +1 -0
  88. pipewright_mlops-1.0.0/src/pipewright/build/compile.py +165 -0
  89. pipewright_mlops-1.0.0/src/pipewright/build/images.py +226 -0
  90. pipewright_mlops-1.0.0/src/pipewright/build/spec_patch.py +81 -0
  91. pipewright_mlops-1.0.0/src/pipewright/build/templates/__init__.py +1 -0
  92. pipewright_mlops-1.0.0/src/pipewright/build/templates/components.Dockerfile +32 -0
  93. pipewright_mlops-1.0.0/src/pipewright/build/templates/pipewright_runtime.py +682 -0
  94. pipewright_mlops-1.0.0/src/pipewright/build/templates/ray.Dockerfile +31 -0
  95. pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-engine-requirements.txt +9 -0
  96. pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-engine.Dockerfile +40 -0
  97. pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-evaluation-requirements.txt +10 -0
  98. pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-evaluation.Dockerfile +35 -0
  99. pipewright_mlops-1.0.0/src/pipewright/checks/__init__.py +1 -0
  100. pipewright_mlops-1.0.0/src/pipewright/checks/doctor.py +666 -0
  101. pipewright_mlops-1.0.0/src/pipewright/checks/kfp_version.py +94 -0
  102. pipewright_mlops-1.0.0/src/pipewright/checks/ray_version.py +61 -0
  103. pipewright_mlops-1.0.0/src/pipewright/cli/__init__.py +5 -0
  104. pipewright_mlops-1.0.0/src/pipewright/cli/main.py +786 -0
  105. pipewright_mlops-1.0.0/src/pipewright/config/__init__.py +22 -0
  106. pipewright_mlops-1.0.0/src/pipewright/config/document.py +97 -0
  107. pipewright_mlops-1.0.0/src/pipewright/config/environment.py +156 -0
  108. pipewright_mlops-1.0.0/src/pipewright/config/io.py +38 -0
  109. pipewright_mlops-1.0.0/src/pipewright/config/merge.py +29 -0
  110. pipewright_mlops-1.0.0/src/pipewright/config/ml.py +287 -0
  111. pipewright_mlops-1.0.0/src/pipewright/config/models.py +140 -0
  112. pipewright_mlops-1.0.0/src/pipewright/config/reference.py +274 -0
  113. pipewright_mlops-1.0.0/src/pipewright/config/references.py +51 -0
  114. pipewright_mlops-1.0.0/src/pipewright/config/resolve.py +378 -0
  115. pipewright_mlops-1.0.0/src/pipewright/config/schema.py +110 -0
  116. pipewright_mlops-1.0.0/src/pipewright/config/schemas/__init__.py +1 -0
  117. pipewright_mlops-1.0.0/src/pipewright/config/schemas/environment.schema.json +52 -0
  118. pipewright_mlops-1.0.0/src/pipewright/config/schemas/manifest.schema.json +728 -0
  119. pipewright_mlops-1.0.0/src/pipewright/config/schemas/reference.yaml +326 -0
  120. pipewright_mlops-1.0.0/src/pipewright/config/schemas/usecase.schema.json +816 -0
  121. pipewright_mlops-1.0.0/src/pipewright/data/__init__.py +1 -0
  122. pipewright_mlops-1.0.0/src/pipewright/data/erase.py +134 -0
  123. pipewright_mlops-1.0.0/src/pipewright/data/erase_gcs.py +56 -0
  124. pipewright_mlops-1.0.0/src/pipewright/errors.py +18 -0
  125. pipewright_mlops-1.0.0/src/pipewright/gcp/__init__.py +6 -0
  126. pipewright_mlops-1.0.0/src/pipewright/gcp/bigquery.py +63 -0
  127. pipewright_mlops-1.0.0/src/pipewright/gcp/session.py +154 -0
  128. pipewright_mlops-1.0.0/src/pipewright/gcp/storage.py +56 -0
  129. pipewright_mlops-1.0.0/src/pipewright/kube.py +89 -0
  130. pipewright_mlops-1.0.0/src/pipewright/launcher/__init__.py +1 -0
  131. pipewright_mlops-1.0.0/src/pipewright/launcher/app.py +323 -0
  132. pipewright_mlops-1.0.0/src/pipewright/launcher/pull.py +138 -0
  133. pipewright_mlops-1.0.0/src/pipewright/launcher/registry.py +91 -0
  134. pipewright_mlops-1.0.0/src/pipewright/launcher/routing.py +169 -0
  135. pipewright_mlops-1.0.0/src/pipewright/models/__init__.py +1 -0
  136. pipewright_mlops-1.0.0/src/pipewright/models/deploy.py +172 -0
  137. pipewright_mlops-1.0.0/src/pipewright/models/gates.py +113 -0
  138. pipewright_mlops-1.0.0/src/pipewright/models/kserve.py +302 -0
  139. pipewright_mlops-1.0.0/src/pipewright/models/registry.py +128 -0
  140. pipewright_mlops-1.0.0/src/pipewright/monitoring/__init__.py +0 -0
  141. pipewright_mlops-1.0.0/src/pipewright/monitoring/status.py +61 -0
  142. pipewright_mlops-1.0.0/src/pipewright/observability/__init__.py +0 -0
  143. pipewright_mlops-1.0.0/src/pipewright/observability/events.py +69 -0
  144. pipewright_mlops-1.0.0/src/pipewright/pipelines_ui.py +97 -0
  145. pipewright_mlops-1.0.0/src/pipewright/plugins.py +43 -0
  146. pipewright_mlops-1.0.0/src/pipewright/ray_clusters/__init__.py +5 -0
  147. pipewright_mlops-1.0.0/src/pipewright/ray_clusters/service.py +407 -0
  148. pipewright_mlops-1.0.0/src/pipewright/ray_clusters/settings.py +186 -0
  149. pipewright_mlops-1.0.0/src/pipewright/release/__init__.py +1 -0
  150. pipewright_mlops-1.0.0/src/pipewright/release/contract.py +208 -0
  151. pipewright_mlops-1.0.0/src/pipewright/release/promote.py +509 -0
  152. pipewright_mlops-1.0.0/src/pipewright/release/publish.py +113 -0
  153. pipewright_mlops-1.0.0/src/pipewright/release/service.py +227 -0
  154. pipewright_mlops-1.0.0/src/pipewright/release/verify.py +103 -0
  155. pipewright_mlops-1.0.0/src/pipewright/retraining/__init__.py +21 -0
  156. pipewright_mlops-1.0.0/src/pipewright/retraining/cases.py +272 -0
  157. pipewright_mlops-1.0.0/src/pipewright/retraining/client.py +145 -0
  158. pipewright_mlops-1.0.0/src/pipewright/retraining/identity.py +73 -0
  159. pipewright_mlops-1.0.0/src/pipewright/retraining/service.py +243 -0
  160. pipewright_mlops-1.0.0/src/pipewright/retraining/store.py +99 -0
  161. pipewright_mlops-1.0.0/src/pipewright/retraining/summary.py +137 -0
  162. pipewright_mlops-1.0.0/src/pipewright/runs/__init__.py +0 -0
  163. pipewright_mlops-1.0.0/src/pipewright/runs/launch.py +106 -0
  164. pipewright_mlops-1.0.0/src/pipewright/runs/sources.py +36 -0
  165. pipewright_mlops-1.0.0/src/pipewright/scaffold/__init__.py +1 -0
  166. pipewright_mlops-1.0.0/src/pipewright/scaffold/ci.py +128 -0
  167. pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/action.yml +62 -0
  168. pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-operate.yml +123 -0
  169. pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-pr.yml +95 -0
  170. pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-release.yml +66 -0
  171. pipewright_mlops-1.0.0/src/pipewright/scaffold/from_bq.py +123 -0
  172. pipewright_mlops-1.0.0/src/pipewright/scaffold/new_usecase.py +101 -0
  173. pipewright_mlops-1.0.0/src/pipewright/schedules/__init__.py +1 -0
  174. pipewright_mlops-1.0.0/src/pipewright/schedules/fire.py +47 -0
  175. pipewright_mlops-1.0.0/src/pipewright/schedules/kubernetes.py +143 -0
  176. pipewright_mlops-1.0.0/src/pipewright/schedules/sync.py +144 -0
  177. pipewright_mlops-1.0.0/src/pipewright/synthetic/__init__.py +20 -0
  178. pipewright_mlops-1.0.0/src/pipewright/synthetic/config.py +277 -0
  179. pipewright_mlops-1.0.0/src/pipewright/synthetic/pipeline.py +59 -0
  180. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/__init__.py +5 -0
  181. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/engines.py +170 -0
  182. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/evaluation.py +234 -0
  183. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/gates.py +89 -0
  184. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/relational.py +151 -0
  185. pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/rules.py +183 -0
  186. pipewright_mlops-1.0.0/src/pipewright/synthetic/synthetic_steps.py +317 -0
  187. pipewright_mlops-1.0.0/src/pipewright/tables/__init__.py +1 -0
  188. pipewright_mlops-1.0.0/src/pipewright/tables/external.py +85 -0
  189. pipewright_mlops-1.0.0/src/pipewright/tables/plan.py +157 -0
  190. pipewright_mlops-1.0.0/src/pipewright/tables/sync.py +103 -0
  191. pipewright_mlops-1.0.0/src/pipewright/testing/__init__.py +0 -0
  192. pipewright_mlops-1.0.0/src/pipewright/testing/runner.py +29 -0
  193. pipewright_mlops-1.0.0/tests/__init__.py +0 -0
  194. pipewright_mlops-1.0.0/tests/conftest.py +134 -0
  195. pipewright_mlops-1.0.0/tests/test_alerts.py +132 -0
  196. pipewright_mlops-1.0.0/tests/test_ci_and_run.py +100 -0
  197. pipewright_mlops-1.0.0/tests/test_config.py +191 -0
  198. pipewright_mlops-1.0.0/tests/test_config_ml.py +191 -0
  199. pipewright_mlops-1.0.0/tests/test_features.py +357 -0
  200. pipewright_mlops-1.0.0/tests/test_gates_and_names.py +104 -0
  201. pipewright_mlops-1.0.0/tests/test_kfp.py +301 -0
  202. pipewright_mlops-1.0.0/tests/test_kfp_version.py +50 -0
  203. pipewright_mlops-1.0.0/tests/test_kserve.py +136 -0
  204. pipewright_mlops-1.0.0/tests/test_launcher.py +180 -0
  205. pipewright_mlops-1.0.0/tests/test_launcher_pull.py +62 -0
  206. pipewright_mlops-1.0.0/tests/test_manifest_compat.py +82 -0
  207. pipewright_mlops-1.0.0/tests/test_ops.py +98 -0
  208. pipewright_mlops-1.0.0/tests/test_outputs.py +144 -0
  209. pipewright_mlops-1.0.0/tests/test_pipelines_ui.py +48 -0
  210. pipewright_mlops-1.0.0/tests/test_platform_hooks.py +257 -0
  211. pipewright_mlops-1.0.0/tests/test_promote.py +311 -0
  212. pipewright_mlops-1.0.0/tests/test_ray.py +449 -0
  213. pipewright_mlops-1.0.0/tests/test_release_parts.py +220 -0
  214. pipewright_mlops-1.0.0/tests/test_retraining.py +344 -0
  215. pipewright_mlops-1.0.0/tests/test_scaffold.py +69 -0
  216. pipewright_mlops-1.0.0/tests/test_schedules_kubernetes.py +121 -0
  217. pipewright_mlops-1.0.0/tests/test_sources.py +46 -0
  218. pipewright_mlops-1.0.0/tests/test_synthetic.py +478 -0
  219. pipewright_mlops-1.0.0/tests/test_tables_and_compile.py +223 -0
  220. pipewright_mlops-1.0.0/tests/test_yaml_help.py +183 -0
  221. pipewright_mlops-1.0.0/uv.lock +2120 -0
@@ -0,0 +1,5 @@
1
+ # The launcher image copies only the built wheel (images/launcher.Dockerfile: COPY dist/). Send nothing else to the
2
+ # build context, so local files (credentials, .env, .git, virtual environments) can never reach an image.
3
+ *
4
+ !dist/
5
+ dist/*.tar.gz
@@ -0,0 +1,6 @@
1
+ # Store text files with LF line endings for every contributor; shell scripts must stay LF to run in CI.
2
+ * text=auto eol=lf
3
+ *.sh text eol=lf
4
+ *.csv text eol=lf
5
+ *.png binary
6
+ *.whl binary
@@ -0,0 +1,64 @@
1
+ # Pipewright product CI: every pull request and push to main, and the first stage of release.yml.
2
+ # Lint and tests, the example's tests, the wheel (contents checked, installed fresh, smoke tested), the launcher
3
+ # image built from that wheel (not pushed). The wheel is kept as the `dist` artifact for release.yml.
4
+ name: ci
5
+
6
+ on:
7
+ pull_request:
8
+ push:
9
+ branches: [main]
10
+ workflow_call:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ test:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v5
21
+ with:
22
+ python-version: "3.11"
23
+ - run: uv sync --all-extras
24
+ - run: uv run ruff check .
25
+ - run: uv run pytest -q
26
+ - name: Example use case tests
27
+ run: uv run --no-project --with-requirements examples/churn/tests/requirements.txt python -m pytest examples/churn/tests -q
28
+
29
+ package:
30
+ needs: test
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - uses: astral-sh/setup-uv@v5
35
+ with:
36
+ python-version: "3.11"
37
+ - name: Build wheel and sdist
38
+ run: uv build --out-dir dist
39
+ - name: Check wheel contents
40
+ run: uv run --no-project python scripts/check_package.py dist
41
+ - name: Install the wheel in a fresh environment and smoke test it
42
+ run: |
43
+ python3 -m venv "$RUNNER_TEMP/smoke"
44
+ "$RUNNER_TEMP/smoke/bin/pip" install "$(ls dist/pipewright_mlops-*-py3-none-any.whl)[gcp]"
45
+ bash scripts/smoke_test.sh "$RUNNER_TEMP/smoke/bin/pipewright"
46
+ - uses: actions/upload-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+ if-no-files-found: error
51
+
52
+ launcher-image:
53
+ needs: package
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+ - uses: actions/download-artifact@v4
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+ - name: Build the launcher image from the wheel (not pushed)
62
+ run: |
63
+ rm -f dist/*.tar.gz
64
+ docker build -f images/launcher.Dockerfile -t pipewright-launcher:ci .
@@ -0,0 +1,132 @@
1
+ # Pipewright release: push a tag v<version> (the version in pyproject.toml) to publish that version on GitHub.
2
+ #
3
+ # ci the whole of ci.yml: tests, wheel, smoke test, launcher image build
4
+ # version the tag must be v<pyproject version>, or v<pyproject version>-rehearsal.<n> for a rehearsal
5
+ # publish-launcher the launcher image, built from the released wheel -> GitHub Container Registry
6
+ # (ghcr.io/<owner>/pipewright-launcher:<version>), linked to this repository so it inherits the
7
+ # repository's access; platform teams copy it by digest into their environment's own registry
8
+ # github-release the GitHub release: the wheel and sdist as files, and the launcher image digest
9
+ # publish-pypi the same wheel and sdist to PyPI (pipewright-mlops), real versions only, after the GitHub
10
+ # release; PyPI Trusted Publishing, so no API token is stored anywhere
11
+ #
12
+ # Keyless: GitHub's built-in token publishes the image and the release, and PyPI trusts this workflow through
13
+ # OpenID Connect (a trusted publisher for repository fluxorsoft/pipewright, workflow release.yml, environment pypi). A rehearsal
14
+ # (v<version>-rehearsal.<n>) publishes a pre-release and an image tagged <version>-rehearsal.<n>, to be deleted
15
+ # afterwards. A real version is published once: do not delete and re-create its release.
16
+ name: release
17
+
18
+ on:
19
+ push:
20
+ tags: ["v*"]
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ concurrency:
26
+ group: release
27
+ cancel-in-progress: false
28
+
29
+ jobs:
30
+ ci:
31
+ uses: ./.github/workflows/ci.yml
32
+
33
+ version:
34
+ runs-on: ubuntu-latest
35
+ outputs:
36
+ version: ${{ steps.v.outputs.version }}
37
+ image_tag: ${{ steps.v.outputs.image_tag }}
38
+ prerelease: ${{ steps.v.outputs.prerelease }}
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - id: v
42
+ env:
43
+ TAG: ${{ github.ref_name }}
44
+ run: |
45
+ version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
46
+ case "$TAG" in
47
+ "v$version") image_tag="$version"; prerelease=false ;;
48
+ "v$version"-rehearsal.[0-9]*) image_tag="${TAG#v}"; prerelease=true ;;
49
+ *) echo "::error::tag $TAG is neither v$version nor v$version-rehearsal.<n> (pyproject.toml version $version)"
50
+ exit 1 ;;
51
+ esac
52
+ case "$version" in *dev*|*a*|*b*|*rc*) prerelease=true ;; esac
53
+ { echo "version=$version"; echo "image_tag=$image_tag"; echo "prerelease=$prerelease"; } >> "$GITHUB_OUTPUT"
54
+
55
+ publish-launcher:
56
+ needs: [ci, version]
57
+ runs-on: ubuntu-latest
58
+ permissions:
59
+ contents: read
60
+ packages: write
61
+ outputs:
62
+ image: ${{ steps.push.outputs.image }}
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+ - uses: actions/download-artifact@v4
66
+ with:
67
+ name: dist
68
+ path: dist/
69
+ - name: Build from the released wheel and push to GitHub Container Registry
70
+ id: push
71
+ env:
72
+ VERSION: ${{ needs.version.outputs.version }}
73
+ TAG: ${{ needs.version.outputs.image_tag }}
74
+ TOKEN: ${{ github.token }}
75
+ run: |
76
+ rm -f dist/*.tar.gz
77
+ image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/pipewright-launcher"
78
+ echo "$TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
79
+ docker build -f images/launcher.Dockerfile --build-arg VERSION="$VERSION" \
80
+ --label "org.opencontainers.image.source=https://github.com/$GITHUB_REPOSITORY" -t "$image:$TAG" .
81
+ docker push "$image:$TAG"
82
+ ref="$(docker inspect --format='{{index .RepoDigests 0}}' "$image:$TAG")"
83
+ echo "image=$ref" >> "$GITHUB_OUTPUT"
84
+ echo "Launcher image: \`$ref\`" >> "$GITHUB_STEP_SUMMARY"
85
+
86
+ github-release:
87
+ needs: [version, publish-launcher]
88
+ runs-on: ubuntu-latest
89
+ permissions:
90
+ contents: write
91
+ steps:
92
+ - uses: actions/download-artifact@v4
93
+ with:
94
+ name: dist
95
+ path: dist/
96
+ - name: Create the GitHub release
97
+ env:
98
+ GH_TOKEN: ${{ github.token }}
99
+ VERSION: ${{ needs.version.outputs.version }}
100
+ IMAGE: ${{ needs.publish-launcher.outputs.image }}
101
+ PRERELEASE: ${{ needs.version.outputs.prerelease }}
102
+ run: |
103
+ cat > notes.md <<EOF
104
+ Pipewright $VERSION
105
+
106
+ - **Python package:** download the wheel below (\`gh release download $GITHUB_REF_NAME -R $GITHUB_REPOSITORY\`)
107
+ and install it, or pin \`pipewright-mlops[gcp]==$VERSION\` from PyPI or your organisation's package index.
108
+ - **Launcher image:** copy it into your environment's own registry by digest, then pin that digest in the
109
+ platform's Terraform:
110
+ \`$IMAGE\`
111
+
112
+ What changed: CHANGELOG.md. What stays stable: STABILITY.md.
113
+ EOF
114
+ flag=""; [ "$PRERELEASE" = true ] && flag="--prerelease"
115
+ gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "Pipewright $GITHUB_REF_NAME" \
116
+ --notes-file notes.md $flag
117
+
118
+ publish-pypi:
119
+ needs: [version, github-release]
120
+ if: needs.version.outputs.prerelease == 'false' # rehearsals never reach PyPI: a version there is final
121
+ runs-on: ubuntu-latest
122
+ environment:
123
+ name: pypi
124
+ url: https://pypi.org/project/pipewright-mlops/${{ needs.version.outputs.version }}/
125
+ permissions:
126
+ id-token: write # PyPI Trusted Publishing
127
+ steps:
128
+ - uses: actions/download-artifact@v4
129
+ with:
130
+ name: dist
131
+ path: dist/
132
+ - uses: pypa/gh-action-pypi-publish@v1.14.2 # twine 7: accepts metadata 2.5 (hatchling)
@@ -0,0 +1,36 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ venv/
7
+
8
+ # Build output. Anchored: src/pipewright/build/ is a package and must stay tracked.
9
+ /dist/
10
+ /build/
11
+
12
+ # Tool caches
13
+ .pytest_cache/
14
+ .ruff_cache/
15
+ .mypy_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Pipewright's per-use-case build output (compiled templates, image contexts)
20
+ .pipewright/
21
+
22
+ # Local settings and secrets: never commit credentials, tokens or CA bundles
23
+ .env
24
+ .env.*
25
+ *.pem
26
+ *.key
27
+ credentials*.json
28
+ *-key.json
29
+ service-account*.json
30
+
31
+ # Editors and operating systems
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ .DS_Store
36
+ Thumbs.db
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to Pipewright. The format follows [Keep a Changelog](https://keepachangelog.com/), and versions
4
+ follow [semantic versioning](https://semver.org/); [STABILITY.md](STABILITY.md) says what it covers.
5
+
6
+ ## [1.0.0] - 2026-10-18
7
+
8
+ First release, on GitHub and on PyPI (`pipewright-mlops`). First published on GitHub on 2026-10-17 and re-cut to
9
+ include synthetic data and, for PyPI, a README whose links work outside the repository.
10
+
11
+ ### Added
12
+
13
+ - **Help with the YAML.** `pipewright explain <key>` (description, type, allowed values, default, whether it may vary
14
+ per environment); editor schemas in `.pipewright-schemas/` and a `yaml-language-server` line in every generated
15
+ YAML file, for completion, hover help and underlined mistakes (`pipewright schemas` for existing repos); every use
16
+ case key described in `config/schemas/reference.yaml`.
17
+ - **Minimal example** (`new-usecase --from minimal`): the churn training code with only the required configuration.
18
+ - **Data contract from BigQuery** (`new-usecase --from-bq <project.dataset.table> --label <column>`): drafts
19
+ `config/data.yaml` and the raw table's schema from a table or view.
20
+ - **Evaluation gates checked by Pipewright.** `model approve` and `deploy-model` recompute the metric gates, slice
21
+ gates and champion margin from the version's evaluation record (`evaluation.json`) and refuse a version that fails;
22
+ `model list` shows each version's result.
23
+ - **Write-once release contracts** and component image tags derived from the release, so re-releases are
24
+ idempotent and immutable tags work. Contract metadata (owner, objective, description, contacts, model) for tools
25
+ without a repository checkout.
26
+ - **Per-tenant pipeline roots.** Runs write their artifacts under `pipeline-root/<tenant>/<usecase>/` of the
27
+ artifacts bucket, so each tenant's runner can be granted its own prefix only.
28
+ - **`pipewright promote`.** A tested release, and optionally its approved model, from one environment to the next:
29
+ destination policies first, images by digest, templates checked equal (or an accepted, recorded difference), model
30
+ files verified, the contract written once with `promoted_from`, the pointer moved last; idempotent; a
31
+ `release.promoted` event. Models carry the release that trained them (a `release` label).
32
+ - **Retrain decisions.** Drift or a performance drop opens a decision case instead of retraining: modes `off`,
33
+ `propose` and `candidate`; a decision pack (what drifted, the trend, data checks, the champion, what retraining
34
+ would use); approver groups with a quorum; snoozes and declines that expire; re-opening when things get worse;
35
+ escalation and a weekly digest; decision events and an opt-in per-tenant feed; `pipewright retrain list | show |
36
+ approve | decline | snooze`; launcher routes `/retraining/propose`, `/retraining/decide`, `/retraining/tick`.
37
+ - **Features from feature tables.** `data.features_from` names BigQuery feature tables and their features; the
38
+ run's source is then the entity table and `read_source` joins each feature as it was at the row's cut-off
39
+ (the newest value at or before it), in one BigQuery query with the environment's key. Scoring without the
40
+ cut-off column uses the latest values. A `data.features.resolved` event, `validate` and `doctor` checks, and
41
+ `new-usecase --from features`.
42
+ - **Feature groups as a feature source.** A `data.features_from` source may name a Feature Store feature group; the
43
+ Feature Registry gives its table and columns at each run. The manifest can list a tenant's feature datasets and
44
+ require feature tables (`features.required`); `validate`, `doctor` and `read_source` enforce both.
45
+ - **Ray on Agent Platform steps.** `steps.<step>.ray` gives a step a Ray cluster for one run, within the platform's
46
+ limits (manifest `ray`, per environment and tenant), created by the launcher as the runner identity with the
47
+ environment's key and Private Service Connect network attachment, and always deleted (by the step, or by the
48
+ launcher's tick). `pipewright release` builds the use case's Ray image; `ray_cluster()` and `ray_job()` in the
49
+ component runtime; a Ray client version check; `new-usecase --from ray`; a `ray.cluster.created` event records
50
+ what each cluster used.
51
+ - **Private-environment support.** The manifest `security` block (CMEK key, BigQuery key, network, reserved IP
52
+ ranges) is applied to every pipeline job, model, endpoint and BigQuery query; run requests over Pub/Sub
53
+ (`launcher.run_requests_topic`, `/pubsub/run`); events published through the regional Pub/Sub endpoint.
54
+ - **Manifest forward compatibility.** Manifest keys this version does not know are ignored with a warning
55
+ (`validate`, `doctor`, the launcher's log) instead of failing; an optional `requires` (for example `">=1.1"`)
56
+ makes an older version refuse the manifest with a clear message.
57
+ - **`predict` in the generated operate workflow** (`pipewright ci init`): runs `pipewright model predict` against the
58
+ use case's endpoint or InferenceService with the `instances` input, inline JSON or a JSON file in the repository
59
+ (for example `usecases/<name>/tests/predict.json`); the predictions go to the job summary.
60
+ - **CLI plugins** through the `pipewright.commands` entry point group.
61
+
62
+ ### Added: private GKE
63
+
64
+ - **Orchestrator `kfp`: Kubeflow Pipelines on private GKE.** The same use case YAML, compiled templates and release
65
+ contracts run on Kubeflow Pipelines inside the cluster, submitted through the Kubernetes API (no exposed endpoint)
66
+ as each tenant's Kubernetes service account, bound to its runner identity. Tasks inside `dsl.OneOf` branches run
67
+ uncached on Kubeflow (it cannot resolve a cached branch).
68
+ - **Ray on KubeRay.** On GKE a step's cluster is a RayCluster per run on the platform's Ray node pool, reached
69
+ inside the cluster; or a tenant's **standing** cluster (`ray.mode: standing`), long-lived and autoscaling, to
70
+ which steps submit with their release's code. The release records the mode it was built for.
71
+ - **Online serving on KServe** (`model.serving.target: kserve`): the registered container and its Agent Platform
72
+ environment as an InferenceService in the tenant's serving namespace, so requests and responses match Agent
73
+ Platform endpoints; gates, approvals, rolling updates, undeploy; exposure in the cluster or an internal load
74
+ balancer. `pipewright model predict` for either platform.
75
+ - **Schedules as Kubernetes CronJobs** where the manifest says `schedules.backend: kubernetes`.
76
+ - **The launcher as a pull worker** (`launcher.runtime: gke`): the same routes fed from the same subscriptions,
77
+ for a launcher inside the cluster; `launcher.audiences` so releases keep working across a switch.
78
+ - **`pipewright pipelines-ui`**: the Kubeflow Pipelines UI on localhost, read only, through the cluster's API.
79
+ - A small Kubernetes client (`pipewright.kube`); credentials ask for `userinfo.email` (GKE names callers by email).
80
+
81
+ ### Added: synthetic data
82
+
83
+ - **Off until a team switches it on.** A `synthetic` section in the use case turns it on, per environment; without
84
+ it nothing is built, compiled or released for it, and templates and digests are unchanged.
85
+ - **Learned inside the environment.** A built-in `synthetic` pipeline (`pipewright run --pipeline synthetic`, or a
86
+ schedule with `pipeline: synthetic`): the source is read as training reads it, a real holdout is kept back,
87
+ candidates are generated in parallel (by default the MOSTLY AI Synthetic Data SDK in local mode, which makes no
88
+ network calls, at sampling temperatures 1.0 and 1.3; SDV as an engine a team installs itself), and each is scored
89
+ on fidelity, utility (a model trained on synthetic data, tested on the real holdout) and privacy (exact copies,
90
+ distance to the closest record, singling-out, linkability and inference attacks). The best candidate that passes
91
+ every gate is published; otherwise nothing is.
92
+ - **Gates per purpose** (`test-data`, `sharing`), recomputed by Pipewright; `pipewright synthetic gates` lists them;
93
+ a use case may make privacy gates stricter, never looser. The platform's `policies.synthetic` in the manifest sets
94
+ the allowed purposes and engines, purposes that need differential privacy, gate floors and required approvers.
95
+ - **Never learned:** keys and personal-data columns get new values from column rules.
96
+ - **Relational and sequential** (`mode: relational`): the entity table with the feature tables of
97
+ `data.features_from` and the event tables in `synthetic.sequences`, learned together. Keys stay consistent across
98
+ tables, each history keeps its timing against the cut-off, and the gates are measured on the point-in-time join of
99
+ the synthetic tables; each table goes to its own output table.
100
+ - **Without real data:** `pipewright synthetic generate` writes rows from the data contract and the column rules,
101
+ for development, CI and demos.
102
+ - **Released like any pipeline:** `pipewright release` builds two images (the engine, and the evaluation), recorded
103
+ in the contract by digest, verified, and copied by `promote`. `doctor` checks the section against the live
104
+ environment. Events `synthetic.evaluated`, `synthetic.rejected` and `synthetic.generated`.
105
+
106
+ ### Changed
107
+
108
+ Compared with the pre-release builds early users ran:
109
+
110
+ - `retraining.enabled: true` now means `retraining.mode: propose`: drift no longer requests training directly.
111
+ - Decision emails show the summary as a code block, so line breaks and commands are kept.
112
+ - `.yml` configuration files are refused with a message to rename them to `.yaml`, instead of being ignored.
113
+ - Licensed under the Apache License 2.0.
114
+ - Distribution name `pipewright-mlops` (PyPI); the import and the command stay `pipewright`.
115
+
116
+ ### Fixed
117
+
118
+ - **`alerts sync` keeps its own notification channels.** Another email channel for the same address (a platform's
119
+ own alerts) made every sync try to repoint the use case's policies; the channel Pipewright made now wins.
120
+ - **Steps with only `retries` or `cache`** no longer get an empty `resources` block in the compiled template: Agent
121
+ Platform ran them on its sized-step backend, where the metadata server issues no ID tokens.
122
+ - **A release reuses a contract only when its images and compiled templates are unchanged**, so a Pipewright
123
+ upgrade that changes how templates compile is released (as `<commit>-<hash>`) instead of silently reusing the old
124
+ template.
125
+ - **Generated CI workflows fail when a command fails.** Steps run as `bash -eo pipefail`, so a `pipewright ...`
126
+ piped to `tee` no longer passes when the command fails.
127
+ - A pipeline job that cannot join its Agent Platform experiment (for example in a new project) is reported as
128
+ submitted with a warning, instead of failing the trigger and being retried as a duplicate.