nextpipe 0.7.0.dev0__tar.gz → 0.7.3.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 (114) hide show
  1. nextpipe-0.7.3.dev0/.github/workflows/lint.yml +23 -0
  2. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.github/workflows/release.yml +18 -7
  3. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.github/workflows/test.yml +7 -8
  4. nextpipe-0.7.3.dev0/.python-version +1 -0
  5. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/PKG-INFO +23 -17
  6. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/README.md +19 -10
  7. nextpipe-0.7.3.dev0/nextpipe/__about__.py +1 -0
  8. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/decorators.py +12 -3
  9. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/flow.py +24 -10
  10. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/schema.py +54 -1
  11. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/pyproject.toml +6 -5
  12. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/README.md +4 -4
  13. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/README.md +2 -2
  14. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/main.py +1 -1
  15. nextpipe-0.7.3.dev0/tests/apps/echo/requirements.txt +1 -0
  16. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/README.md +2 -2
  17. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/app.yaml +3 -0
  18. nextpipe-0.7.3.dev0/tests/apps/echo-multi/requirements.txt +1 -0
  19. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.json.golden +1 -1
  20. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.json.golden +12 -18
  21. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.py +7 -2
  22. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.json.golden +4 -4
  23. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.json.golden +4 -4
  24. nextpipe-0.7.3.dev0/tests/test_flow.py +104 -0
  25. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_integration.py +23 -14
  26. nextpipe-0.7.3.dev0/tests/test_schema.py +69 -0
  27. nextpipe-0.7.3.dev0/uv.lock +1228 -0
  28. nextpipe-0.7.0.dev0/.github/workflows/lint.yml +0 -24
  29. nextpipe-0.7.0.dev0/.markdownlint.jsonc +0 -51
  30. nextpipe-0.7.0.dev0/.readthedocs.yaml +0 -19
  31. nextpipe-0.7.0.dev0/docs/examples/basic-chained-workflow.md +0 -124
  32. nextpipe-0.7.0.dev0/docs/examples/complex-workflow-csv.md +0 -345
  33. nextpipe-0.7.0.dev0/docs/examples/complex-workflow.md +0 -264
  34. nextpipe-0.7.0.dev0/docs/examples/ensemble-workflow.md +0 -663
  35. nextpipe-0.7.0.dev0/docs/examples/fanout-workflow.md +0 -187
  36. nextpipe-0.7.0.dev0/docs/examples/multifile-workflow.md +0 -180
  37. nextpipe-0.7.0.dev0/docs/images/active-plan.png +0 -0
  38. nextpipe-0.7.0.dev0/docs/images/api-keys.png +0 -0
  39. nextpipe-0.7.0.dev0/docs/images/app-diagram.png +0 -0
  40. nextpipe-0.7.0.dev0/docs/images/apps-orchestrate-full.png +0 -0
  41. nextpipe-0.7.0.dev0/docs/images/apps-orchestrate.png +0 -0
  42. nextpipe-0.7.0.dev0/docs/images/innovator.png +0 -0
  43. nextpipe-0.7.0.dev0/docs/images/logo-180.png +0 -0
  44. nextpipe-0.7.0.dev0/docs/images/new-run-orchestrate.png +0 -0
  45. nextpipe-0.7.0.dev0/docs/images/nextmv-favicon.svg +0 -4
  46. nextpipe-0.7.0.dev0/docs/images/plans.png +0 -0
  47. nextpipe-0.7.0.dev0/docs/images/pushed-app.png +0 -0
  48. nextpipe-0.7.0.dev0/docs/images/routing-ortools-pyvroom-selected.png +0 -0
  49. nextpipe-0.7.0.dev0/docs/images/routing-selected.png +0 -0
  50. nextpipe-0.7.0.dev0/docs/images/run-metadata.png +0 -0
  51. nextpipe-0.7.0.dev0/docs/images/run-summary.png +0 -0
  52. nextpipe-0.7.0.dev0/docs/images/run-workflow.png +0 -0
  53. nextpipe-0.7.0.dev0/docs/images/secrets-collection-orchestrate.png +0 -0
  54. nextpipe-0.7.0.dev0/docs/images/workflow-diagram.png +0 -0
  55. nextpipe-0.7.0.dev0/docs/index.md +0 -411
  56. nextpipe-0.7.0.dev0/docs/reference/config.md +0 -5
  57. nextpipe-0.7.0.dev0/docs/reference/decorators.md +0 -5
  58. nextpipe-0.7.0.dev0/docs/reference/echo-multi.md +0 -154
  59. nextpipe-0.7.0.dev0/docs/reference/echo.md +0 -114
  60. nextpipe-0.7.0.dev0/docs/reference/flow.md +0 -6
  61. nextpipe-0.7.0.dev0/docs/reference/graph.md +0 -6
  62. nextpipe-0.7.0.dev0/docs/reference/schema.md +0 -5
  63. nextpipe-0.7.0.dev0/docs/reference/threads.md +0 -5
  64. nextpipe-0.7.0.dev0/docs/reference/uplink.md +0 -5
  65. nextpipe-0.7.0.dev0/docs/reference/utils.md +0 -5
  66. nextpipe-0.7.0.dev0/docs/requirements.txt +0 -4
  67. nextpipe-0.7.0.dev0/docs/tutorials/orchestrate-multiple-models.md +0 -1986
  68. nextpipe-0.7.0.dev0/mkdocs.yml +0 -144
  69. nextpipe-0.7.0.dev0/nextpipe/__about__.py +0 -1
  70. nextpipe-0.7.0.dev0/tests/apps/echo/requirements.txt +0 -1
  71. nextpipe-0.7.0.dev0/tests/apps/echo-multi/requirements.txt +0 -1
  72. nextpipe-0.7.0.dev0/tests/test_schema.py +0 -27
  73. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.gitignore +0 -0
  74. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.nextmv/bump_requirements.sh +0 -0
  75. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.prettierrc.yml +0 -0
  76. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/LICENSE.md +0 -0
  77. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/RELEASE.md +0 -0
  78. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/__init__.py +0 -0
  79. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/config.py +0 -0
  80. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/constants.py +0 -0
  81. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/graph.py +0 -0
  82. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/threads.py +0 -0
  83. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/uplink.py +0 -0
  84. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/utils.py +0 -0
  85. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe.code-workspace +0 -0
  86. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/.gitignore +0 -0
  87. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/__init__.py +0 -0
  88. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/.gitignore +0 -0
  89. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/app.yaml +0 -0
  90. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/.gitignore +0 -0
  91. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/inputs/data.csv +0 -0
  92. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/inputs/input.xlsx +0 -0
  93. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/main.py +0 -0
  94. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/app.yaml +0 -0
  95. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/main.py +0 -0
  96. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/requirements.txt +0 -0
  97. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.json +0 -0
  98. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.py +0 -0
  99. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.json +0 -0
  100. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.json.golden +0 -0
  101. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.py +0 -0
  102. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.json +0 -0
  103. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/fail.py +0 -0
  104. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.json +0 -0
  105. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.py +0 -0
  106. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.json +0 -0
  107. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.py +0 -0
  108. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/inputs/data.csv +0 -0
  109. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/inputs/input.xlsx +0 -0
  110. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/multifile.py +0 -0
  111. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_graph.py +0 -0
  112. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_threads.py +0 -0
  113. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_uplink.py +0 -0
  114. {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_version.py +0 -0
@@ -0,0 +1,23 @@
1
+ name: lint
2
+ on: [push]
3
+ jobs:
4
+ lint:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ matrix:
8
+ python-version: ["3.10"]
9
+ steps:
10
+ - name: git clone
11
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
12
+
13
+ - name: install uv
14
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
15
+ with:
16
+ version: "0.9.29"
17
+ python-version: ${{ matrix.python-version }}
18
+
19
+ - name: install the project
20
+ run: uv sync --locked --dev
21
+
22
+ - name: lint with ruff
23
+ run: uv run ruff check --output-format=github .
@@ -3,10 +3,16 @@ run-name: Release by @${{ github.actor }} from ${{ github.ref_name }}
3
3
 
4
4
  on: [push]
5
5
 
6
+ permissions:
7
+ contents: read
8
+
6
9
  jobs:
7
10
  release:
8
11
  if: ${{ github.ref_type == 'branch' }}
9
- uses: nextmv-io/release/.github/workflows/release.yml@develop
12
+ uses: nextmv-io/release/.github/workflows/release.yml@949cbb3b90ab294f8da42cf9c314637deaa578f5 # develop
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
10
16
  with:
11
17
  BRANCH: ${{ github.ref_name }}
12
18
  REPOSITORY: nextpipe
@@ -14,7 +20,10 @@ jobs:
14
20
  PACKAGE_NAME: nextpipe
15
21
  PACKAGE_LOCATION: .
16
22
  VERSION_FILE: __about__.py
17
- secrets: inherit
23
+ secrets:
24
+ SLACK_URL_MISSION_CONTROL: ${{ secrets.SLACK_URL_MISSION_CONTROL }}
25
+ NEXTMVBOT_SSH_KEY: ${{ secrets.NEXTMVBOT_SSH_KEY }}
26
+ NEXTMVBOT_SIGNING_KEY: ${{ secrets.NEXTMVBOT_SIGNING_KEY }}
18
27
 
19
28
  publish: # Unfortunately, PyPI publishing does not support reusable workflows, so we must publish here.
20
29
  needs: release
@@ -27,12 +36,13 @@ jobs:
27
36
  id-token: write # This is required for trusted publishing to PyPI
28
37
  steps:
29
38
  - name: git clone ${{ github.ref_name }}
30
- uses: actions/checkout@v4
39
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31
40
  with:
32
41
  ref: ${{ github.ref_name }}
42
+ persist-credentials: false
33
43
 
34
44
  - name: set up Python
35
- uses: actions/setup-python@v5
45
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
36
46
 
37
47
  - name: install dependencies
38
48
  run: |
@@ -43,16 +53,17 @@ jobs:
43
53
  run: python -m build
44
54
 
45
55
  - name: python - publish package distributions to PyPI
46
- uses: pypa/gh-action-pypi-publish@release/v1
56
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
47
57
  with:
48
58
  packages-dir: ./dist
49
59
 
50
60
  notify:
51
61
  needs: [release, publish]
52
62
  if: ${{ needs.release.outputs.RELEASE_NEEDED == 'true' && needs.release.outputs.SHOULD_NOTIFY_SLACK == 'true' }}
53
- uses: nextmv-io/release/.github/workflows/notify-slack.yml@develop
63
+ uses: nextmv-io/release/.github/workflows/notify-slack.yml@949cbb3b90ab294f8da42cf9c314637deaa578f5 # develop
54
64
  with:
55
65
  PACKAGE_NAME: nextpipe
56
66
  VERSION: ${{ needs.release.outputs.VERSION }}
57
67
  REPOSITORY: nextpipe
58
- secrets: inherit
68
+ secrets:
69
+ SLACK_URL_MISSION_CONTROL: ${{ secrets.SLACK_URL_MISSION_CONTROL }}
@@ -8,21 +8,20 @@ jobs:
8
8
  python-version: ["3.10", "3.14"]
9
9
  steps:
10
10
  - name: git clone
11
- uses: actions/checkout@v4
11
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
12
12
 
13
- - name: set up Python ${{ matrix.python-version }}
14
- uses: actions/setup-python@v5
13
+ - name: install uv
14
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
15
15
  with:
16
+ version: "0.9.29"
16
17
  python-version: ${{ matrix.python-version }}
17
18
 
18
- - name: install dependencies
19
- run: |
20
- python -m pip install --upgrade pip
21
- pip install .[dev]
19
+ - name: install the project
20
+ run: uv sync --locked --dev
22
21
 
23
22
  - name: unit tests
24
23
  env:
25
24
  NEXTMV_API_KEY_NEXTPIPE: ${{ secrets.NEXTMV_API_KEY_NEXTPIPE }}
26
25
  run: |
27
26
  export NEXTMV_API_KEY=$NEXTMV_API_KEY_NEXTPIPE
28
- python -m unittest
27
+ uv run pytest
@@ -0,0 +1 @@
1
+ 3.13
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: nextpipe
3
- Version: 0.7.0.dev0
3
+ Version: 0.7.3-dev.0
4
4
  Summary: Framework for Decision Pipeline modeling and execution
5
5
  Project-URL: Homepage, https://www.nextmv.io
6
- Project-URL: Documentation, https://nextpipe.docs.nextmv.io/en/latest/
6
+ Project-URL: Documentation, https://docs.nextmv.io/nextpipe
7
7
  Project-URL: Repository, https://github.com/nextmv-io/nextpipe
8
8
  Author-email: Nextmv <tech@nextmv.io>
9
9
  Maintainer-email: Nextmv <tech@nextmv.io>
@@ -105,17 +105,12 @@ Classifier: Programming Language :: Python :: 3.13
105
105
  Classifier: Programming Language :: Python :: 3.14
106
106
  Requires-Python: >=3.10
107
107
  Requires-Dist: dataclasses-json>=0.6.7
108
- Requires-Dist: nextmv>=1.2.2
108
+ Requires-Dist: nextmv>=1.10.3
109
109
  Requires-Dist: requests>=2.31.0
110
- Provides-Extra: dev
111
- Requires-Dist: goldie>=0.1.8; extra == 'dev'
112
- Requires-Dist: ruff>=0.11.6; extra == 'dev'
113
110
  Description-Content-Type: text/markdown
114
111
 
115
112
  # Nextpipe
116
113
 
117
- <!-- markdownlint-disable MD033 MD013 -->
118
-
119
114
  <p align="center">
120
115
  <a href="https://nextmv.io"><img src="https://cdn.prod.website-files.com/60dee0fad10d14c8ab66dd74/65c66addcd07eed09be35114_blog-banner-what-is-cicd-for-decision-science-p-2000.jpeg" alt="Nextmv" width="45%"></a>
121
116
  </p>
@@ -137,8 +132,6 @@ Description-Content-Type: text/markdown
137
132
  </a>
138
133
  </p>
139
134
 
140
- <!-- markdownlint-enable MD033 MD013 -->
141
-
142
135
  Nextpipe is a Python package that provides a framework for Decision Workflows
143
136
  modeling and execution. It provides first-class support for Workflows in the
144
137
  [Nextmv Platform][nextmv].
@@ -152,13 +145,26 @@ modeling and execution. It provides first-class support for Workflows in the
152
145
 
153
146
  ## Installation
154
147
 
155
- The package is hosted on [PyPI][nextpipe-pypi]. Python `>=3.10` is required.
148
+ The package is hosted on [PyPI][nextpipe-pypi]. Requires Python `>=3.10`.
149
+ Install using the Python package manager of your choice:
156
150
 
157
- Install via `pip`:
151
+ * `uv`
158
152
 
159
- ```bash
160
- pip install nextpipe
161
- ```
153
+ ```bash
154
+ uv add nextpipe
155
+ ```
156
+
157
+ * `pip`
158
+
159
+ ```bash
160
+ pip install nextpipe
161
+ ```
162
+
163
+ * `pipx`
164
+
165
+ ```bash
166
+ pipx install nextpipe
167
+ ```
162
168
 
163
169
  ## Preview
164
170
 
@@ -192,4 +198,4 @@ graph LR
192
198
 
193
199
  [nextpipe-pypi]: https://pypi.org/project/nextpipe/
194
200
  [nextmv]: https://nextmv.io
195
- [docs]: https://nextpipe.docs.nextmv.io/en/latest/
201
+ [docs]: https://docs.nextmv.io/nextpipe
@@ -1,7 +1,5 @@
1
1
  # Nextpipe
2
2
 
3
- <!-- markdownlint-disable MD033 MD013 -->
4
-
5
3
  <p align="center">
6
4
  <a href="https://nextmv.io"><img src="https://cdn.prod.website-files.com/60dee0fad10d14c8ab66dd74/65c66addcd07eed09be35114_blog-banner-what-is-cicd-for-decision-science-p-2000.jpeg" alt="Nextmv" width="45%"></a>
7
5
  </p>
@@ -23,8 +21,6 @@
23
21
  </a>
24
22
  </p>
25
23
 
26
- <!-- markdownlint-enable MD033 MD013 -->
27
-
28
24
  Nextpipe is a Python package that provides a framework for Decision Workflows
29
25
  modeling and execution. It provides first-class support for Workflows in the
30
26
  [Nextmv Platform][nextmv].
@@ -38,13 +34,26 @@ modeling and execution. It provides first-class support for Workflows in the
38
34
 
39
35
  ## Installation
40
36
 
41
- The package is hosted on [PyPI][nextpipe-pypi]. Python `>=3.10` is required.
37
+ The package is hosted on [PyPI][nextpipe-pypi]. Requires Python `>=3.10`.
38
+ Install using the Python package manager of your choice:
42
39
 
43
- Install via `pip`:
40
+ * `uv`
44
41
 
45
- ```bash
46
- pip install nextpipe
47
- ```
42
+ ```bash
43
+ uv add nextpipe
44
+ ```
45
+
46
+ * `pip`
47
+
48
+ ```bash
49
+ pip install nextpipe
50
+ ```
51
+
52
+ * `pipx`
53
+
54
+ ```bash
55
+ pipx install nextpipe
56
+ ```
48
57
 
49
58
  ## Preview
50
59
 
@@ -78,4 +87,4 @@ graph LR
78
87
 
79
88
  [nextpipe-pypi]: https://pypi.org/project/nextpipe/
80
89
  [nextmv]: https://nextmv.io
81
- [docs]: https://nextpipe.docs.nextmv.io/en/latest/
90
+ [docs]: https://docs.nextmv.io/nextpipe
@@ -0,0 +1 @@
1
+ __version__ = "v0.7.3-dev.0"
@@ -798,12 +798,16 @@ class App:
798
798
  The ID of the Nextmv Application to run.
799
799
  instance_id : str, optional
800
800
  The ID of the instance to run. Default is defined by the app on Platform.
801
+ A predecessor step may override this per run by returning an `AppRunConfig`
802
+ carrying its own `instance_id`.
801
803
  options : dict[str, Any], optional
802
804
  The options to pass to the application, by default None.
803
805
  full_result : bool, optional
804
806
  Whether to return the full result including metadata, by default False.
805
807
  run_configuration : nextmv.RunConfiguration, optional
806
- The configuration to apply when running the app, by default None.
808
+ The configuration to apply when running the app, by default None. A
809
+ predecessor step may override this per run by returning an
810
+ `AppRunConfig` carrying its own `run_configuration`.
807
811
  polling_options : Optional[nextmv.PollingOptions], optional
808
812
  Options for polling for the results of the app run, by default _DEFAULT_POLLING_OPTIONS.
809
813
  bypass_result : typing.Any, optional
@@ -884,7 +888,9 @@ def app(
884
888
  app_id : str
885
889
  The ID of the application to run.
886
890
  instance_id : str
887
- The ID of the instance to run. Default is defined by the app on Platform.
891
+ The ID of the instance to run. Default is defined by the app on Platform. A
892
+ predecessor step may override this per run by returning an `AppRunConfig`
893
+ carrying its own `instance_id`.
888
894
  options : dict[str, Any]
889
895
  The options to pass to the application. This is a dictionary of
890
896
  parameter names and values. The values must be JSON serializable.
@@ -894,7 +900,10 @@ def app(
894
900
  this is set to `False`, only the output of the application will be
895
901
  returned.
896
902
  run_configuration : nextmv.RunConfiguration
897
- The configuration to apply when running the app.
903
+ The configuration to apply when running the app. A predecessor step may
904
+ override this per run by returning an `AppRunConfig` carrying its own
905
+ `run_configuration`. In that case, the configuration of the `AppRunConfig`
906
+ replaces this one as a whole, i.e., no fields are inherited from it.
898
907
  polling_options : Optional[nextmv.PollingOptions]
899
908
  Options for polling for the results of the app run. This is used to
900
909
  configure the polling behavior, such as the timeout and backoff
@@ -901,14 +901,15 @@ class Runner:
901
901
  node: FlowNode,
902
902
  inputs: list[object],
903
903
  app_step: decorators.App,
904
- ) -> tuple[dict[str, Any], nextmv.PollingOptions, bool]:
904
+ ) -> tuple[dict[str, Any], nextmv.PollingOptions, bool, str]:
905
905
  """
906
906
  Prepare arguments for running an app step.
907
907
 
908
908
  Returns
909
909
  -------
910
- tuple[dict[str, Any], nextmv.PollingOptions, bool]
911
- A tuple of run kwargs, polling options, and whether the input is dir mode.
910
+ tuple[dict[str, Any], nextmv.PollingOptions, bool, str]
911
+ A tuple of run kwargs, polling options, whether the input is dir mode, and
912
+ the ID of the instance to run.
912
913
  """
913
914
 
914
915
  app_run_name, app_run_description = Runner.__determine_sub_run_info(app_step.app_id, node.id)
@@ -920,6 +921,9 @@ class Runner:
920
921
  if len(inputs) > 1:
921
922
  raise Exception(f"App steps cannot have more than one predecessor, but {node.id} has {len(inputs)}")
922
923
 
924
+ run_configuration = app_step.run_configuration
925
+ instance_id = app_step.instance_id
926
+
923
927
  if isinstance(inputs[0], schema.AppRunConfig):
924
928
  app_run_config: schema.AppRunConfig = inputs[0]
925
929
  input_data = app_run_config.input
@@ -929,6 +933,12 @@ class Runner:
929
933
  app_run_description = app_run_config.description
930
934
  # AppRunConfig options take precedence over decorator options.
931
935
  options = app_step.options | app_run_config.get_options()
936
+ # An AppRunConfig run configuration replaces the decorator one as a whole.
937
+ if app_run_config.run_configuration is not None:
938
+ run_configuration = app_run_config.run_configuration
939
+ # Same for the instance to run on.
940
+ if app_run_config.instance_id:
941
+ instance_id = app_run_config.instance_id
932
942
  elif isinstance(inputs[0], nextmv.RunResult):
933
943
  run_result: nextmv.RunResult = inputs[0]
934
944
  input_data = run_result.output
@@ -953,10 +963,10 @@ class Runner:
953
963
  run_kwargs["input_dir_path"] = input_data
954
964
  else:
955
965
  run_kwargs["input"] = input_data
956
- if app_step.run_configuration is not None:
957
- run_kwargs["configuration"] = app_step.run_configuration
966
+ if run_configuration is not None:
967
+ run_kwargs["configuration"] = run_configuration
958
968
 
959
- return run_kwargs, polling_options, is_dir_mode
969
+ return run_kwargs, polling_options, is_dir_mode, instance_id
960
970
 
961
971
  @staticmethod
962
972
  def __execute_app_run(
@@ -969,6 +979,7 @@ class Runner:
969
979
  polling_options: nextmv.PollingOptions,
970
980
  is_dir_mode: bool,
971
981
  temp_dir: str,
982
+ instance_id: str,
972
983
  ) -> nextmv.RunResult:
973
984
  """
974
985
  Execute an app run or return bypass output wrapped as a run result.
@@ -1000,7 +1011,7 @@ class Runner:
1000
1011
  user_email="unavailable",
1001
1012
  metadata=nextmv.Metadata(
1002
1013
  application_id=app_step.app_id or "unavailable",
1003
- application_instance_id=app_step.instance_id or "unavailable",
1014
+ application_instance_id=instance_id or "unavailable",
1004
1015
  application_version_id="unavailable",
1005
1016
  created_at=datetime.datetime.now(),
1006
1017
  duration=0.0,
@@ -1067,15 +1078,17 @@ class Runner:
1067
1078
  if node.parent.definition.is_app():
1068
1079
  app_step: decorators.App = node.parent.definition.app
1069
1080
 
1070
- run_kwargs, polling_options, is_dir_mode = Runner.__prepare_app_run_args(node, inputs, app_step)
1081
+ run_kwargs, polling_options, is_dir_mode, instance_id = Runner.__prepare_app_run_args(
1082
+ node, inputs, app_step
1083
+ )
1071
1084
 
1072
1085
  # Prepare the application itself.
1073
1086
  app = Application(
1074
1087
  client=client,
1075
1088
  id=app_step.app_id,
1076
1089
  )
1077
- if app_step.instance_id is not None and app_step.instance_id != "":
1078
- app.default_instance_id = app_step.instance_id
1090
+ if instance_id is not None and instance_id != "":
1091
+ app.default_instance_id = instance_id
1079
1092
 
1080
1093
  # We always supply an output directory path in case of implicit multi-file output
1081
1094
  temp_dir = tempfile.mkdtemp(prefix="nextpipe_output_")
@@ -1092,6 +1105,7 @@ class Runner:
1092
1105
  polling_options=polling_options,
1093
1106
  is_dir_mode=is_dir_mode,
1094
1107
  temp_dir=temp_dir,
1108
+ instance_id=instance_id,
1095
1109
  )
1096
1110
 
1097
1111
  finally: # Make sure we clean up temp dir on failure too
@@ -14,11 +14,31 @@ AppRunConfig
14
14
  from dataclasses import dataclass, field
15
15
  from typing import Any
16
16
 
17
- from dataclasses_json import dataclass_json
17
+ import nextmv
18
+ from dataclasses_json import config, dataclass_json
18
19
 
19
20
  from . import utils
20
21
 
21
22
 
23
+ def _encode_run_configuration(
24
+ run_configuration: nextmv.RunConfiguration | None,
25
+ ) -> dict[str, Any] | None:
26
+ """Encode a run configuration for JSON serialization."""
27
+
28
+ return run_configuration.to_dict() if run_configuration is not None else None
29
+
30
+
31
+ def _decode_run_configuration(
32
+ run_configuration: dict[str, Any] | nextmv.RunConfiguration | None,
33
+ ) -> nextmv.RunConfiguration | None:
34
+ """Decode a run configuration from its JSON representation."""
35
+
36
+ if run_configuration is None or isinstance(run_configuration, nextmv.RunConfiguration):
37
+ return run_configuration
38
+
39
+ return nextmv.RunConfiguration.from_dict(run_configuration)
40
+
41
+
22
42
  @dataclass_json
23
43
  @dataclass
24
44
  class AppOption:
@@ -79,6 +99,16 @@ class AppRunConfig:
79
99
  `AppOption` instances, or, simply as a dictionary of key-value pairs.
80
100
  name : str, optional
81
101
  Name for the run, by default None.
102
+ description : str, optional
103
+ Description for the run, by default None.
104
+ run_configuration : nextmv.RunConfiguration, optional
105
+ The configuration to apply when running the app, by default None. If given, it
106
+ replaces the `run_configuration` of the `app` decorator for this run. Note that
107
+ it replaces it as a whole, i.e., no fields are inherited from the decorator's
108
+ configuration.
109
+ instance_id : str, optional
110
+ The ID of the instance to run, by default None. If given, it replaces the
111
+ `instance_id` of the `app` decorator for this run.
82
112
 
83
113
  Examples
84
114
  --------
@@ -88,6 +118,17 @@ class AppRunConfig:
88
118
  ... options={"threads": 4},
89
119
  ... name="my-run"
90
120
  ... )
121
+
122
+ The run configuration and instance can be chosen per run, e.g., to pick an
123
+ execution class based on the size of the scenario at hand.
124
+
125
+ >>> import nextmv
126
+ >>> from nextpipe import AppRunConfig
127
+ >>> config = AppRunConfig(
128
+ ... input={"data": [1, 2, 3]},
129
+ ... run_configuration=nextmv.RunConfiguration(execution_class="8c16gb12h"),
130
+ ... instance_id="tuned-for-large",
131
+ ... )
91
132
  """
92
133
 
93
134
  input: dict[str, Any] | str | Any
@@ -100,6 +141,18 @@ class AppRunConfig:
100
141
  """Name for the run."""
101
142
  description: str | None = None
102
143
  """Description for the run."""
144
+ run_configuration: nextmv.RunConfiguration | None = field(
145
+ default=None,
146
+ metadata=config(
147
+ encoder=_encode_run_configuration,
148
+ decoder=_decode_run_configuration,
149
+ ),
150
+ )
151
+ """The configuration to apply when running the app. Replaces the `run_configuration`
152
+ of the `app` decorator as a whole, if given."""
153
+ instance_id: str | None = None
154
+ """The ID of the instance to run. Replaces the `instance_id` of the `app` decorator,
155
+ if given."""
103
156
 
104
157
  def get_options(self) -> dict[str, Any]:
105
158
  """
@@ -17,7 +17,7 @@ classifiers = [
17
17
  ]
18
18
  dependencies = [
19
19
  "requests>=2.31.0",
20
- "nextmv>=1.2.2",
20
+ "nextmv>=1.10.3",
21
21
  "dataclasses-json>=0.6.7",
22
22
  ]
23
23
  description = "Framework for Decision Pipeline modeling and execution"
@@ -47,7 +47,7 @@ requires-python = ">=3.10"
47
47
 
48
48
  [project.urls]
49
49
  Homepage = "https://www.nextmv.io"
50
- Documentation = "https://nextpipe.docs.nextmv.io/en/latest/"
50
+ Documentation = "https://docs.nextmv.io/nextpipe"
51
51
  Repository = "https://github.com/nextmv-io/nextpipe"
52
52
 
53
53
  [tool.ruff]
@@ -68,8 +68,9 @@ max-complexity = 20
68
68
  [tool.hatch.version]
69
69
  path = "nextpipe/__about__.py"
70
70
 
71
- [project.optional-dependencies]
71
+ [dependency-groups]
72
72
  dev = [
73
- "ruff>=0.11.6",
74
- "goldie>=0.1.8",
73
+ "goldie==0.1.8",
74
+ "pytest==9.0.2",
75
+ "ruff==0.16.3",
75
76
  ]
@@ -9,13 +9,13 @@ Setup the custom apps as follows:
9
9
  ```bash
10
10
  # Setup echo app
11
11
  cd apps/echo
12
- nextmv app create -a echo -n "Echo" || true
13
- nextmv app push -a echo
12
+ nextmv cloud app create -a echo -n "Echo" || true
13
+ nextmv cloud app push -a echo
14
14
  cd ../..
15
15
  # Setup echo-multi app
16
16
  cd apps/echo-multi
17
- nextmv app create -a echo-multi -n "Echo Multi" || true
18
- nextmv app push -a echo-multi
17
+ nextmv cloud app create -a echo-multi -n "Echo Multi" || true
18
+ nextmv cloud app push -a echo-multi
19
19
  cd ../..
20
20
  ```
21
21
 
@@ -14,6 +14,6 @@ echo '{"hello": "world!"}' | python main.py
14
14
  ## Push to Nextmv
15
15
 
16
16
  ```bash
17
- nextmv app create -a echo -n "JSON echo" -d "This is a sample app that reads JSON from the input and writes it to the output."
18
- nextmv app push -a echo
17
+ nextmv cloud app create -a echo -n "JSON echo" -d "This is a sample app that reads JSON from the input and writes it to the output."
18
+ nextmv cloud app push -a echo
19
19
  ```
@@ -12,6 +12,6 @@ output = nextmv.Output(
12
12
  "args": sys.argv[1:],
13
13
  },
14
14
  },
15
- statistics={"run": {"duration": time.time() - before}},
15
+ metrics={"run": {"duration": time.time() - before}},
16
16
  )
17
17
  nextmv.write(output)
@@ -0,0 +1 @@
1
+ nextmv==1.10.1
@@ -14,6 +14,6 @@ python main.py -threads 10 -duration 10
14
14
  ## Push to Nextmv
15
15
 
16
16
  ```bash
17
- nextmv app create -a echo-multi -n "JSON echo multi-file" -d "This is a sample app that reads input files and echoes them as output."
18
- nextmv app push -a echo-multi
17
+ nextmv cloud app create -a echo-multi -n "JSON echo multi-file" -d "This is a sample app that reads input files and echoes them as output."
18
+ nextmv cloud app push -a echo-multi
19
19
  ```
@@ -4,3 +4,6 @@ python:
4
4
  pip-requirements: requirements.txt
5
5
  files:
6
6
  - main.py
7
+ configuration:
8
+ content:
9
+ format: multi-file
@@ -0,0 +1 @@
1
+ nextmv==1.10.1
@@ -11,7 +11,7 @@
11
11
  "args": []
12
12
  }
13
13
  },
14
- "statistics": {
14
+ "metrics": {
15
15
  "run": {
16
16
  "duration": 0.123
17
17
  }
@@ -1,7 +1,5 @@
1
1
  {
2
2
  "options": {
3
- "input": "",
4
- "output": "",
5
3
  "duration": 30
6
4
  },
7
5
  "solution": {
@@ -51,22 +49,18 @@
51
49
  ],
52
50
  "unplanned": []
53
51
  },
54
- "statistics": {
55
- "run": {
56
- "duration": "0.123"
57
- },
52
+ "assets": [],
53
+ "metrics": {
54
+ "duration": "0.123",
55
+ "objective_value": 287,
56
+ "solution_found": true,
57
+ "activated_vehicles": 1,
58
+ "max_route_duration": 287,
59
+ "max_stops_in_vehicle": 2,
60
+ "min_stops_in_vehicle": 2,
58
61
  "result": {
59
- "value": 287.0,
60
- "custom": {
61
- "solution_found": true,
62
- "activated_vehicles": 1,
63
- "max_route_duration": 287,
64
- "max_stops_in_vehicle": 2,
65
- "min_stops_in_vehicle": 2
66
- },
62
+ "value": 287,
67
63
  "duration": "0.123"
68
- },
69
- "schema": "v1"
70
- },
71
- "assets": []
64
+ }
65
+ }
72
66
  }
@@ -43,12 +43,17 @@ class Flow(FlowSpec):
43
43
  ):
44
44
  """Aggregates the results."""
45
45
  results = results_nextroute + [result_ortools, result_pyvroom]
46
+
47
+ # Define value extractor for both output formats of the solvers.
48
+ def get_value(sol: dict):
49
+ return sol["metrics"]["result"]["value"] if "metrics" in sol else sol["statistics"]["result"]["value"]
50
+
46
51
  best_solution_idx = min(
47
52
  range(len(results)),
48
- key=lambda i: results[i]["statistics"]["result"]["value"],
53
+ key=lambda i: get_value(results[i]),
49
54
  )
50
55
 
51
- values = [result["statistics"]["result"]["value"] for result in results]
56
+ values = [get_value(result) for result in results]
52
57
  values.sort()
53
58
  log(f"Values: {values}")
54
59