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.
- nextpipe-0.7.3.dev0/.github/workflows/lint.yml +23 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.github/workflows/release.yml +18 -7
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.github/workflows/test.yml +7 -8
- nextpipe-0.7.3.dev0/.python-version +1 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/PKG-INFO +23 -17
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/README.md +19 -10
- nextpipe-0.7.3.dev0/nextpipe/__about__.py +1 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/decorators.py +12 -3
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/flow.py +24 -10
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/schema.py +54 -1
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/pyproject.toml +6 -5
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/README.md +4 -4
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/README.md +2 -2
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/main.py +1 -1
- nextpipe-0.7.3.dev0/tests/apps/echo/requirements.txt +1 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/README.md +2 -2
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/app.yaml +3 -0
- nextpipe-0.7.3.dev0/tests/apps/echo-multi/requirements.txt +1 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.json.golden +1 -1
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.json.golden +12 -18
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.py +7 -2
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.json.golden +4 -4
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.json.golden +4 -4
- nextpipe-0.7.3.dev0/tests/test_flow.py +104 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_integration.py +23 -14
- nextpipe-0.7.3.dev0/tests/test_schema.py +69 -0
- nextpipe-0.7.3.dev0/uv.lock +1228 -0
- nextpipe-0.7.0.dev0/.github/workflows/lint.yml +0 -24
- nextpipe-0.7.0.dev0/.markdownlint.jsonc +0 -51
- nextpipe-0.7.0.dev0/.readthedocs.yaml +0 -19
- nextpipe-0.7.0.dev0/docs/examples/basic-chained-workflow.md +0 -124
- nextpipe-0.7.0.dev0/docs/examples/complex-workflow-csv.md +0 -345
- nextpipe-0.7.0.dev0/docs/examples/complex-workflow.md +0 -264
- nextpipe-0.7.0.dev0/docs/examples/ensemble-workflow.md +0 -663
- nextpipe-0.7.0.dev0/docs/examples/fanout-workflow.md +0 -187
- nextpipe-0.7.0.dev0/docs/examples/multifile-workflow.md +0 -180
- nextpipe-0.7.0.dev0/docs/images/active-plan.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/api-keys.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/app-diagram.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/apps-orchestrate-full.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/apps-orchestrate.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/innovator.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/logo-180.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/new-run-orchestrate.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/nextmv-favicon.svg +0 -4
- nextpipe-0.7.0.dev0/docs/images/plans.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/pushed-app.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/routing-ortools-pyvroom-selected.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/routing-selected.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/run-metadata.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/run-summary.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/run-workflow.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/secrets-collection-orchestrate.png +0 -0
- nextpipe-0.7.0.dev0/docs/images/workflow-diagram.png +0 -0
- nextpipe-0.7.0.dev0/docs/index.md +0 -411
- nextpipe-0.7.0.dev0/docs/reference/config.md +0 -5
- nextpipe-0.7.0.dev0/docs/reference/decorators.md +0 -5
- nextpipe-0.7.0.dev0/docs/reference/echo-multi.md +0 -154
- nextpipe-0.7.0.dev0/docs/reference/echo.md +0 -114
- nextpipe-0.7.0.dev0/docs/reference/flow.md +0 -6
- nextpipe-0.7.0.dev0/docs/reference/graph.md +0 -6
- nextpipe-0.7.0.dev0/docs/reference/schema.md +0 -5
- nextpipe-0.7.0.dev0/docs/reference/threads.md +0 -5
- nextpipe-0.7.0.dev0/docs/reference/uplink.md +0 -5
- nextpipe-0.7.0.dev0/docs/reference/utils.md +0 -5
- nextpipe-0.7.0.dev0/docs/requirements.txt +0 -4
- nextpipe-0.7.0.dev0/docs/tutorials/orchestrate-multiple-models.md +0 -1986
- nextpipe-0.7.0.dev0/mkdocs.yml +0 -144
- nextpipe-0.7.0.dev0/nextpipe/__about__.py +0 -1
- nextpipe-0.7.0.dev0/tests/apps/echo/requirements.txt +0 -1
- nextpipe-0.7.0.dev0/tests/apps/echo-multi/requirements.txt +0 -1
- nextpipe-0.7.0.dev0/tests/test_schema.py +0 -27
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.gitignore +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.nextmv/bump_requirements.sh +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/.prettierrc.yml +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/LICENSE.md +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/RELEASE.md +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/__init__.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/config.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/constants.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/graph.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/threads.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/uplink.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe/utils.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/nextpipe.code-workspace +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/.gitignore +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/__init__.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/.gitignore +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo/app.yaml +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/.gitignore +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/inputs/data.csv +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/inputs/input.xlsx +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/apps/echo-multi/main.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/app.yaml +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/main.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/deploy/requirements.txt +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.json +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/appapp.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.json +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.json.golden +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/chain.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/complex.json +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/fail.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.json +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach-2-pred.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.json +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/foreach.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/inputs/data.csv +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/inputs/input.xlsx +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/pipelines/multifile.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_graph.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_threads.py +0 -0
- {nextpipe-0.7.0.dev0 → nextpipe-0.7.3.dev0}/tests/test_uplink.py +0 -0
- {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:
|
|
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@
|
|
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@
|
|
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@
|
|
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:
|
|
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@
|
|
11
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
12
12
|
|
|
13
|
-
- name:
|
|
14
|
-
uses:
|
|
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
|
|
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
|
-
|
|
27
|
+
uv run pytest
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: nextpipe
|
|
3
|
-
Version: 0.7.0
|
|
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://
|
|
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.
|
|
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
|
|
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
|
-
|
|
151
|
+
* `uv`
|
|
158
152
|
|
|
159
|
-
```bash
|
|
160
|
-
|
|
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://
|
|
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
|
|
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
|
-
|
|
40
|
+
* `uv`
|
|
44
41
|
|
|
45
|
-
```bash
|
|
46
|
-
|
|
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://
|
|
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,
|
|
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
|
|
957
|
-
run_kwargs["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=
|
|
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(
|
|
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
|
|
1078
|
-
app.default_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
|
-
|
|
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.
|
|
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://
|
|
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
|
-
[
|
|
71
|
+
[dependency-groups]
|
|
72
72
|
dev = [
|
|
73
|
-
"
|
|
74
|
-
"
|
|
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
|
```
|
|
@@ -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
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nextmv==1.10.1
|
|
@@ -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
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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
|
-
|
|
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]
|
|
53
|
+
key=lambda i: get_value(results[i]),
|
|
49
54
|
)
|
|
50
55
|
|
|
51
|
-
values = [result
|
|
56
|
+
values = [get_value(result) for result in results]
|
|
52
57
|
values.sort()
|
|
53
58
|
log(f"Values: {values}")
|
|
54
59
|
|