strands-osmo 0.1.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.
- strands_osmo-0.1.0/LICENSE +17 -0
- strands_osmo-0.1.0/PKG-INFO +278 -0
- strands_osmo-0.1.0/README.md +235 -0
- strands_osmo-0.1.0/pyproject.toml +89 -0
- strands_osmo-0.1.0/setup.cfg +4 -0
- strands_osmo-0.1.0/strands_osmo/__init__.py +102 -0
- strands_osmo-0.1.0/strands_osmo/tools/__init__.py +94 -0
- strands_osmo-0.1.0/strands_osmo/tools/_common.py +161 -0
- strands_osmo-0.1.0/strands_osmo/tools/app_create.py +29 -0
- strands_osmo-0.1.0/strands_osmo/tools/app_list.py +23 -0
- strands_osmo-0.1.0/strands_osmo/tools/bucket_list.py +16 -0
- strands_osmo-0.1.0/strands_osmo/tools/cookbook_fetch.py +80 -0
- strands_osmo-0.1.0/strands_osmo/tools/data_download.py +27 -0
- strands_osmo-0.1.0/strands_osmo/tools/data_list.py +27 -0
- strands_osmo-0.1.0/strands_osmo/tools/data_upload.py +35 -0
- strands_osmo-0.1.0/strands_osmo/tools/dataset_describe.py +23 -0
- strands_osmo-0.1.0/strands_osmo/tools/dataset_list.py +26 -0
- strands_osmo-0.1.0/strands_osmo/tools/doctor.py +57 -0
- strands_osmo-0.1.0/strands_osmo/tools/login.py +31 -0
- strands_osmo-0.1.0/strands_osmo/tools/pool_list.py +27 -0
- strands_osmo-0.1.0/strands_osmo/tools/profile_list.py +16 -0
- strands_osmo-0.1.0/strands_osmo/tools/resources_list.py +16 -0
- strands_osmo-0.1.0/strands_osmo/tools/task_describe.py +19 -0
- strands_osmo-0.1.0/strands_osmo/tools/task_list.py +18 -0
- strands_osmo-0.1.0/strands_osmo/tools/version.py +13 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_cancel.py +23 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_exec.py +26 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_list.py +34 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_logs.py +35 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_render.py +45 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_status.py +22 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_submit.py +57 -0
- strands_osmo-0.1.0/strands_osmo/tools/workflow_validate.py +117 -0
- strands_osmo-0.1.0/strands_osmo.egg-info/PKG-INFO +278 -0
- strands_osmo-0.1.0/strands_osmo.egg-info/SOURCES.txt +39 -0
- strands_osmo-0.1.0/strands_osmo.egg-info/dependency_links.txt +1 -0
- strands_osmo-0.1.0/strands_osmo.egg-info/requires.txt +19 -0
- strands_osmo-0.1.0/strands_osmo.egg-info/top_level.txt +1 -0
- strands_osmo-0.1.0/tests/test_doctor.py +17 -0
- strands_osmo-0.1.0/tests/test_imports.py +41 -0
- strands_osmo-0.1.0/tests/test_validate.py +85 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 Cagatay Cali
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strands-osmo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NVIDIA OSMO workflow orchestration for Strands Agents - submit, monitor, and debug Physical AI pipelines from natural language.
|
|
5
|
+
Author-email: Cagatay Cali <cagataycali@icloud.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/cagataycali/strands-osmo
|
|
8
|
+
Project-URL: Repository, https://github.com/cagataycali/strands-osmo
|
|
9
|
+
Project-URL: Issues, https://github.com/cagataycali/strands-osmo/issues
|
|
10
|
+
Project-URL: Documentation, https://cagataycali.github.io/strands-osmo/
|
|
11
|
+
Keywords: strands,agents,osmo,nvidia,kubernetes,workflow,orchestration,physical-ai,robotics,isaac-sim,gr00t,edge,jetson
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: strands-agents
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: jinja2>=3.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff; extra == "dev"
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Requires-Dist: mkdocs-material; extra == "docs"
|
|
35
|
+
Requires-Dist: pymdown-extensions; extra == "docs"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: strands-agents-tools; extra == "all"
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == "all"
|
|
39
|
+
Requires-Dist: ruff; extra == "all"
|
|
40
|
+
Requires-Dist: mkdocs-material; extra == "all"
|
|
41
|
+
Requires-Dist: pymdown-extensions; extra == "all"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# strands-osmo
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/strands-osmo/)
|
|
47
|
+
[](https://cagataycali.github.io/strands-osmo/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
[](https://github.com/cagataycali/awesome-strands-agents)
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="strands-osmo-logo.svg" alt="Strands OSMO" width="180">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
**[NVIDIA OSMO](https://github.com/NVIDIA/OSMO) workflow orchestration for [Strands Agents](https://strandsagents.com) - submit, monitor, and debug Physical AI pipelines from natural language.**
|
|
57
|
+
|
|
58
|
+
OSMO is NVIDIA's open-source Kubernetes-native control plane for Physical AI. It runs heterogeneous compute (training GPUs, simulation GPUs, edge devices) from a single YAML spec - used in production for **GR00T**, **Isaac Lab**, **Isaac Dexterity**, **Isaac Sim**, **Isaac ROS**.
|
|
59
|
+
|
|
60
|
+
This package wraps the production `osmo` CLI as **22 Strands tools** so an agent can drive the entire workflow lifecycle in plain English.
|
|
61
|
+
|
|
62
|
+
> Sister project: [`strands-cosmos`](https://github.com/cagataycali/strands-cosmos) (Cosmos VLM provider + 21 generation/edge tools).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install strands-osmo
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You also need the **OSMO CLI** on `PATH`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl -fsSL https://raw.githubusercontent.com/NVIDIA/OSMO/main/install.sh | bash
|
|
76
|
+
osmo login # one-time OAuth
|
|
77
|
+
osmo version # confirm
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Verify everything from your agent:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from strands_osmo import osmo_doctor
|
|
84
|
+
osmo_doctor()
|
|
85
|
+
# → {"osmo_present": True, "version_ok": True, "logged_in": True, ...}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
### Discover resources, submit a workflow
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from strands import Agent
|
|
96
|
+
from strands_osmo import (
|
|
97
|
+
osmo_doctor, osmo_pool_list, osmo_resources_list,
|
|
98
|
+
osmo_workflow_validate, osmo_workflow_submit, osmo_workflow_status,
|
|
99
|
+
osmo_workflow_logs, osmo_workflow_cancel,
|
|
100
|
+
osmo_cookbook_fetch,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
agent = Agent(tools=[
|
|
104
|
+
osmo_doctor, osmo_pool_list, osmo_resources_list,
|
|
105
|
+
osmo_workflow_validate, osmo_workflow_submit, osmo_workflow_status,
|
|
106
|
+
osmo_workflow_logs, osmo_workflow_cancel,
|
|
107
|
+
osmo_cookbook_fetch,
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
agent("""
|
|
111
|
+
Find me an idle H100 pool with at least 4 GPUs free,
|
|
112
|
+
fetch the GR00T fine-tune recipe from the cookbook,
|
|
113
|
+
validate it, then submit it. Report the workflow ID.
|
|
114
|
+
""")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### One-shot pipeline
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from strands_osmo import osmo_workflow_submit
|
|
121
|
+
|
|
122
|
+
osmo_workflow_submit(
|
|
123
|
+
workflow_yaml="train.yaml",
|
|
124
|
+
pool="h100-prod",
|
|
125
|
+
set_vars={"num_gpu": 4, "epochs": 10},
|
|
126
|
+
priority="LOW", # bypass quota, run on idle capacity
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Tools
|
|
133
|
+
|
|
134
|
+
| Category | Tools | Wraps |
|
|
135
|
+
|-------------|----------------------------------------------------------------------------------------------------------|-------|
|
|
136
|
+
| **Auth** | `osmo_doctor`, `osmo_login`, `osmo_version` | env / `osmo login` / `osmo version` |
|
|
137
|
+
| **Resources** | `osmo_pool_list`, `osmo_resources_list`, `osmo_profile_list`, `osmo_bucket_list` | `osmo pool/resources/profile/bucket list` |
|
|
138
|
+
| **Workflow** | `osmo_workflow_submit`, `osmo_workflow_list`, `osmo_workflow_status`, `osmo_workflow_cancel`, `osmo_workflow_logs`, `osmo_workflow_exec` | `osmo workflow ...` |
|
|
139
|
+
| **Task** | `osmo_task_list`, `osmo_task_describe` | `osmo task ...` |
|
|
140
|
+
| **Data** | `osmo_data_upload`, `osmo_data_download`, `osmo_data_list` | `osmo data ...` |
|
|
141
|
+
| **Dataset** | `osmo_dataset_list`, `osmo_dataset_describe` | `osmo dataset ...` |
|
|
142
|
+
| **App** | `osmo_app_list`, `osmo_app_create` | `osmo app ...` |
|
|
143
|
+
| **Cookbook** | `osmo_cookbook_fetch` | GitHub raw / local clone |
|
|
144
|
+
| **Spec** | `osmo_workflow_validate`, `osmo_workflow_render` | local YAML + Jinja |
|
|
145
|
+
|
|
146
|
+
**Total: 25 tools** (22 OSMO-CLI-backed + 3 local helpers).
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from strands_osmo import osmo_pool_list, osmo_workflow_submit
|
|
150
|
+
|
|
151
|
+
osmo_pool_list(mode="free")
|
|
152
|
+
# → JSON: pools, free GPUs, quota state
|
|
153
|
+
|
|
154
|
+
osmo_workflow_submit("train.yaml", pool="h100-prod", set_vars={"epochs": 10})
|
|
155
|
+
# → workflow ID(s)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Why use it inside an agent?
|
|
161
|
+
|
|
162
|
+
OSMO already has a great CLI. The point of `strands-osmo` is letting an
|
|
163
|
+
agent **chain** OSMO ops with reasoning:
|
|
164
|
+
|
|
165
|
+
| Without an agent | With an agent |
|
|
166
|
+
|-----------------------------------------------------|---------------------------------------------------------|
|
|
167
|
+
| `osmo pool list` → eyeball | "Find me an idle H100" |
|
|
168
|
+
| Edit YAML by hand to fit quota | "Cap memory to fit the smallest A100 node, then submit" |
|
|
169
|
+
| `osmo workflow logs <id>` → grep for stack trace | "Why did workflow X fail and how do I fix it?" |
|
|
170
|
+
| `osmo workflow submit && watch status` | "Submit, wait until done, summarize results, retrain on failure" |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Architecture
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
strands_osmo/
|
|
178
|
+
├── __init__.py # exports 25 tools
|
|
179
|
+
└── tools/
|
|
180
|
+
├── _common.py # osmo CLI runner + ToolResult helpers
|
|
181
|
+
├── doctor.py # env probe (osmo binary, login, profile)
|
|
182
|
+
├── login.py # osmo login
|
|
183
|
+
├── version.py # osmo version
|
|
184
|
+
├── pool_list.py # osmo pool list [--mode free]
|
|
185
|
+
├── resources_list.py # osmo resources list
|
|
186
|
+
├── profile_list.py # osmo profile list
|
|
187
|
+
├── bucket_list.py # osmo bucket list
|
|
188
|
+
├── workflow_submit.py # osmo workflow submit
|
|
189
|
+
├── workflow_list.py # osmo workflow list
|
|
190
|
+
├── workflow_status.py # osmo workflow describe
|
|
191
|
+
├── workflow_cancel.py # osmo workflow cancel
|
|
192
|
+
├── workflow_logs.py # osmo workflow logs
|
|
193
|
+
├── workflow_exec.py # osmo workflow exec
|
|
194
|
+
├── task_list.py # osmo task list
|
|
195
|
+
├── task_describe.py # osmo task describe
|
|
196
|
+
├── data_upload.py # osmo data upload
|
|
197
|
+
├── data_download.py # osmo data download
|
|
198
|
+
├── data_list.py # osmo data list
|
|
199
|
+
├── dataset_list.py # osmo dataset list
|
|
200
|
+
├── dataset_describe.py # osmo dataset describe
|
|
201
|
+
├── app_list.py # osmo app list
|
|
202
|
+
├── app_create.py # osmo app create
|
|
203
|
+
├── cookbook_fetch.py # OSMO cookbook fetcher
|
|
204
|
+
├── workflow_validate.py# local YAML lint
|
|
205
|
+
└── workflow_render.py # local Jinja render
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Design tenets**:
|
|
209
|
+
|
|
210
|
+
1. **CLI is truth.** Tools shell out to `osmo`, never re-implement its client SDK.
|
|
211
|
+
2. **Thin wrappers.** Each tool ≈ 50 lines, normalizing JSON into Strands `ToolResult`.
|
|
212
|
+
3. **Graceful degradation.** Missing `osmo` binary → `exit 127` ToolResult, not crash.
|
|
213
|
+
4. **No upstream forking.** Use OSMO as-is; it's a separate repo on disk.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Workflow YAML 101 (cheat sheet)
|
|
218
|
+
|
|
219
|
+
```yaml
|
|
220
|
+
workflow:
|
|
221
|
+
name: my-pipeline
|
|
222
|
+
resources:
|
|
223
|
+
default: { cpu: 4, memory: 16Gi, storage: 40Gi, gpu: 1 }
|
|
224
|
+
|
|
225
|
+
tasks:
|
|
226
|
+
- name: train
|
|
227
|
+
image: pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel
|
|
228
|
+
command: ["python", "train.py"]
|
|
229
|
+
args: ["--epochs", "{{epochs}}", "--num-gpu", "{{num_gpu}}"]
|
|
230
|
+
resources: { gpu: "{{num_gpu}}" }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Submit with:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
osmo workflow submit my-pipeline.yaml --pool h100-prod \
|
|
237
|
+
--set num_gpu=4 --set epochs=10
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Or:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
osmo_workflow_submit("my-pipeline.yaml", pool="h100-prod",
|
|
244
|
+
set_vars={"num_gpu": 4, "epochs": 10})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
For the full reference (including `groups`, `inputs`, `outputs.dataset`,
|
|
248
|
+
checkpointing, and platform constraints) see the [OSMO User Guide](https://nvidia.github.io/OSMO/main/user_guide/).
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Development
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
git clone https://github.com/cagataycali/strands-osmo && cd strands-osmo
|
|
256
|
+
pip install -e ".[dev]"
|
|
257
|
+
pytest tests/ # smoke tests
|
|
258
|
+
ruff check . # lint
|
|
259
|
+
ruff format . # format
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## See Also
|
|
265
|
+
|
|
266
|
+
- **OSMO** - <https://github.com/NVIDIA/OSMO>
|
|
267
|
+
- **OSMO docs** - <https://nvidia.github.io/OSMO/main/>
|
|
268
|
+
- **Strands Agents** - <https://strandsagents.com>
|
|
269
|
+
- **strands-cosmos** - <https://github.com/cagataycali/strands-cosmos>
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
Apache 2.0 - same license as upstream OSMO.
|
|
276
|
+
|
|
277
|
+
OSMO is © NVIDIA Corporation. `strands-osmo` is an independent
|
|
278
|
+
community project.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# strands-osmo
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/strands-osmo/)
|
|
4
|
+
[](https://cagataycali.github.io/strands-osmo/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
[](https://github.com/cagataycali/awesome-strands-agents)
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="strands-osmo-logo.svg" alt="Strands OSMO" width="180">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
**[NVIDIA OSMO](https://github.com/NVIDIA/OSMO) workflow orchestration for [Strands Agents](https://strandsagents.com) - submit, monitor, and debug Physical AI pipelines from natural language.**
|
|
14
|
+
|
|
15
|
+
OSMO is NVIDIA's open-source Kubernetes-native control plane for Physical AI. It runs heterogeneous compute (training GPUs, simulation GPUs, edge devices) from a single YAML spec - used in production for **GR00T**, **Isaac Lab**, **Isaac Dexterity**, **Isaac Sim**, **Isaac ROS**.
|
|
16
|
+
|
|
17
|
+
This package wraps the production `osmo` CLI as **22 Strands tools** so an agent can drive the entire workflow lifecycle in plain English.
|
|
18
|
+
|
|
19
|
+
> Sister project: [`strands-cosmos`](https://github.com/cagataycali/strands-cosmos) (Cosmos VLM provider + 21 generation/edge tools).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install strands-osmo
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You also need the **OSMO CLI** on `PATH`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
curl -fsSL https://raw.githubusercontent.com/NVIDIA/OSMO/main/install.sh | bash
|
|
33
|
+
osmo login # one-time OAuth
|
|
34
|
+
osmo version # confirm
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Verify everything from your agent:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from strands_osmo import osmo_doctor
|
|
41
|
+
osmo_doctor()
|
|
42
|
+
# → {"osmo_present": True, "version_ok": True, "logged_in": True, ...}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### Discover resources, submit a workflow
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from strands import Agent
|
|
53
|
+
from strands_osmo import (
|
|
54
|
+
osmo_doctor, osmo_pool_list, osmo_resources_list,
|
|
55
|
+
osmo_workflow_validate, osmo_workflow_submit, osmo_workflow_status,
|
|
56
|
+
osmo_workflow_logs, osmo_workflow_cancel,
|
|
57
|
+
osmo_cookbook_fetch,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
agent = Agent(tools=[
|
|
61
|
+
osmo_doctor, osmo_pool_list, osmo_resources_list,
|
|
62
|
+
osmo_workflow_validate, osmo_workflow_submit, osmo_workflow_status,
|
|
63
|
+
osmo_workflow_logs, osmo_workflow_cancel,
|
|
64
|
+
osmo_cookbook_fetch,
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
agent("""
|
|
68
|
+
Find me an idle H100 pool with at least 4 GPUs free,
|
|
69
|
+
fetch the GR00T fine-tune recipe from the cookbook,
|
|
70
|
+
validate it, then submit it. Report the workflow ID.
|
|
71
|
+
""")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### One-shot pipeline
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from strands_osmo import osmo_workflow_submit
|
|
78
|
+
|
|
79
|
+
osmo_workflow_submit(
|
|
80
|
+
workflow_yaml="train.yaml",
|
|
81
|
+
pool="h100-prod",
|
|
82
|
+
set_vars={"num_gpu": 4, "epochs": 10},
|
|
83
|
+
priority="LOW", # bypass quota, run on idle capacity
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Tools
|
|
90
|
+
|
|
91
|
+
| Category | Tools | Wraps |
|
|
92
|
+
|-------------|----------------------------------------------------------------------------------------------------------|-------|
|
|
93
|
+
| **Auth** | `osmo_doctor`, `osmo_login`, `osmo_version` | env / `osmo login` / `osmo version` |
|
|
94
|
+
| **Resources** | `osmo_pool_list`, `osmo_resources_list`, `osmo_profile_list`, `osmo_bucket_list` | `osmo pool/resources/profile/bucket list` |
|
|
95
|
+
| **Workflow** | `osmo_workflow_submit`, `osmo_workflow_list`, `osmo_workflow_status`, `osmo_workflow_cancel`, `osmo_workflow_logs`, `osmo_workflow_exec` | `osmo workflow ...` |
|
|
96
|
+
| **Task** | `osmo_task_list`, `osmo_task_describe` | `osmo task ...` |
|
|
97
|
+
| **Data** | `osmo_data_upload`, `osmo_data_download`, `osmo_data_list` | `osmo data ...` |
|
|
98
|
+
| **Dataset** | `osmo_dataset_list`, `osmo_dataset_describe` | `osmo dataset ...` |
|
|
99
|
+
| **App** | `osmo_app_list`, `osmo_app_create` | `osmo app ...` |
|
|
100
|
+
| **Cookbook** | `osmo_cookbook_fetch` | GitHub raw / local clone |
|
|
101
|
+
| **Spec** | `osmo_workflow_validate`, `osmo_workflow_render` | local YAML + Jinja |
|
|
102
|
+
|
|
103
|
+
**Total: 25 tools** (22 OSMO-CLI-backed + 3 local helpers).
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from strands_osmo import osmo_pool_list, osmo_workflow_submit
|
|
107
|
+
|
|
108
|
+
osmo_pool_list(mode="free")
|
|
109
|
+
# → JSON: pools, free GPUs, quota state
|
|
110
|
+
|
|
111
|
+
osmo_workflow_submit("train.yaml", pool="h100-prod", set_vars={"epochs": 10})
|
|
112
|
+
# → workflow ID(s)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Why use it inside an agent?
|
|
118
|
+
|
|
119
|
+
OSMO already has a great CLI. The point of `strands-osmo` is letting an
|
|
120
|
+
agent **chain** OSMO ops with reasoning:
|
|
121
|
+
|
|
122
|
+
| Without an agent | With an agent |
|
|
123
|
+
|-----------------------------------------------------|---------------------------------------------------------|
|
|
124
|
+
| `osmo pool list` → eyeball | "Find me an idle H100" |
|
|
125
|
+
| Edit YAML by hand to fit quota | "Cap memory to fit the smallest A100 node, then submit" |
|
|
126
|
+
| `osmo workflow logs <id>` → grep for stack trace | "Why did workflow X fail and how do I fix it?" |
|
|
127
|
+
| `osmo workflow submit && watch status` | "Submit, wait until done, summarize results, retrain on failure" |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Architecture
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
strands_osmo/
|
|
135
|
+
├── __init__.py # exports 25 tools
|
|
136
|
+
└── tools/
|
|
137
|
+
├── _common.py # osmo CLI runner + ToolResult helpers
|
|
138
|
+
├── doctor.py # env probe (osmo binary, login, profile)
|
|
139
|
+
├── login.py # osmo login
|
|
140
|
+
├── version.py # osmo version
|
|
141
|
+
├── pool_list.py # osmo pool list [--mode free]
|
|
142
|
+
├── resources_list.py # osmo resources list
|
|
143
|
+
├── profile_list.py # osmo profile list
|
|
144
|
+
├── bucket_list.py # osmo bucket list
|
|
145
|
+
├── workflow_submit.py # osmo workflow submit
|
|
146
|
+
├── workflow_list.py # osmo workflow list
|
|
147
|
+
├── workflow_status.py # osmo workflow describe
|
|
148
|
+
├── workflow_cancel.py # osmo workflow cancel
|
|
149
|
+
├── workflow_logs.py # osmo workflow logs
|
|
150
|
+
├── workflow_exec.py # osmo workflow exec
|
|
151
|
+
├── task_list.py # osmo task list
|
|
152
|
+
├── task_describe.py # osmo task describe
|
|
153
|
+
├── data_upload.py # osmo data upload
|
|
154
|
+
├── data_download.py # osmo data download
|
|
155
|
+
├── data_list.py # osmo data list
|
|
156
|
+
├── dataset_list.py # osmo dataset list
|
|
157
|
+
├── dataset_describe.py # osmo dataset describe
|
|
158
|
+
├── app_list.py # osmo app list
|
|
159
|
+
├── app_create.py # osmo app create
|
|
160
|
+
├── cookbook_fetch.py # OSMO cookbook fetcher
|
|
161
|
+
├── workflow_validate.py# local YAML lint
|
|
162
|
+
└── workflow_render.py # local Jinja render
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Design tenets**:
|
|
166
|
+
|
|
167
|
+
1. **CLI is truth.** Tools shell out to `osmo`, never re-implement its client SDK.
|
|
168
|
+
2. **Thin wrappers.** Each tool ≈ 50 lines, normalizing JSON into Strands `ToolResult`.
|
|
169
|
+
3. **Graceful degradation.** Missing `osmo` binary → `exit 127` ToolResult, not crash.
|
|
170
|
+
4. **No upstream forking.** Use OSMO as-is; it's a separate repo on disk.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Workflow YAML 101 (cheat sheet)
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
workflow:
|
|
178
|
+
name: my-pipeline
|
|
179
|
+
resources:
|
|
180
|
+
default: { cpu: 4, memory: 16Gi, storage: 40Gi, gpu: 1 }
|
|
181
|
+
|
|
182
|
+
tasks:
|
|
183
|
+
- name: train
|
|
184
|
+
image: pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel
|
|
185
|
+
command: ["python", "train.py"]
|
|
186
|
+
args: ["--epochs", "{{epochs}}", "--num-gpu", "{{num_gpu}}"]
|
|
187
|
+
resources: { gpu: "{{num_gpu}}" }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Submit with:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
osmo workflow submit my-pipeline.yaml --pool h100-prod \
|
|
194
|
+
--set num_gpu=4 --set epochs=10
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Or:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
osmo_workflow_submit("my-pipeline.yaml", pool="h100-prod",
|
|
201
|
+
set_vars={"num_gpu": 4, "epochs": 10})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For the full reference (including `groups`, `inputs`, `outputs.dataset`,
|
|
205
|
+
checkpointing, and platform constraints) see the [OSMO User Guide](https://nvidia.github.io/OSMO/main/user_guide/).
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git clone https://github.com/cagataycali/strands-osmo && cd strands-osmo
|
|
213
|
+
pip install -e ".[dev]"
|
|
214
|
+
pytest tests/ # smoke tests
|
|
215
|
+
ruff check . # lint
|
|
216
|
+
ruff format . # format
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## See Also
|
|
222
|
+
|
|
223
|
+
- **OSMO** - <https://github.com/NVIDIA/OSMO>
|
|
224
|
+
- **OSMO docs** - <https://nvidia.github.io/OSMO/main/>
|
|
225
|
+
- **Strands Agents** - <https://strandsagents.com>
|
|
226
|
+
- **strands-cosmos** - <https://github.com/cagataycali/strands-cosmos>
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
Apache 2.0 - same license as upstream OSMO.
|
|
233
|
+
|
|
234
|
+
OSMO is © NVIDIA Corporation. `strands-osmo` is an independent
|
|
235
|
+
community project.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "strands-osmo"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "NVIDIA OSMO workflow orchestration for Strands Agents - submit, monitor, and debug Physical AI pipelines from natural language."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Cagatay Cali", email = "cagataycali@icloud.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"strands",
|
|
17
|
+
"agents",
|
|
18
|
+
"osmo",
|
|
19
|
+
"nvidia",
|
|
20
|
+
"kubernetes",
|
|
21
|
+
"workflow",
|
|
22
|
+
"orchestration",
|
|
23
|
+
"physical-ai",
|
|
24
|
+
"robotics",
|
|
25
|
+
"isaac-sim",
|
|
26
|
+
"gr00t",
|
|
27
|
+
"edge",
|
|
28
|
+
"jetson",
|
|
29
|
+
]
|
|
30
|
+
classifiers = [
|
|
31
|
+
"Development Status :: 3 - Alpha",
|
|
32
|
+
"Intended Audience :: Developers",
|
|
33
|
+
"License :: OSI Approved :: Apache Software License",
|
|
34
|
+
"Programming Language :: Python :: 3",
|
|
35
|
+
"Programming Language :: Python :: 3.10",
|
|
36
|
+
"Programming Language :: Python :: 3.11",
|
|
37
|
+
"Programming Language :: Python :: 3.12",
|
|
38
|
+
"Programming Language :: Python :: 3.13",
|
|
39
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
40
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
41
|
+
"Topic :: System :: Distributed Computing",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
dependencies = [
|
|
45
|
+
"strands-agents",
|
|
46
|
+
"pyyaml>=6.0",
|
|
47
|
+
"jinja2>=3.0",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.optional-dependencies]
|
|
51
|
+
dev = [
|
|
52
|
+
"pytest>=7.0",
|
|
53
|
+
"pytest-asyncio",
|
|
54
|
+
"ruff",
|
|
55
|
+
]
|
|
56
|
+
docs = [
|
|
57
|
+
"mkdocs-material",
|
|
58
|
+
"pymdown-extensions",
|
|
59
|
+
]
|
|
60
|
+
all = [
|
|
61
|
+
"strands-agents-tools",
|
|
62
|
+
"pytest>=7.0",
|
|
63
|
+
"ruff",
|
|
64
|
+
"mkdocs-material",
|
|
65
|
+
"pymdown-extensions",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[project.urls]
|
|
69
|
+
Homepage = "https://github.com/cagataycali/strands-osmo"
|
|
70
|
+
Repository = "https://github.com/cagataycali/strands-osmo"
|
|
71
|
+
Issues = "https://github.com/cagataycali/strands-osmo/issues"
|
|
72
|
+
Documentation = "https://cagataycali.github.io/strands-osmo/"
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.packages.find]
|
|
75
|
+
where = ["."]
|
|
76
|
+
include = ["strands_osmo*"]
|
|
77
|
+
exclude = ["tests*"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
line-length = 100
|
|
81
|
+
target-version = "py310"
|
|
82
|
+
|
|
83
|
+
[tool.ruff.lint]
|
|
84
|
+
select = ["E", "F", "I", "N", "W"]
|
|
85
|
+
ignore = ["E501"]
|
|
86
|
+
|
|
87
|
+
[tool.pytest.ini_options]
|
|
88
|
+
testpaths = ["tests"]
|
|
89
|
+
python_files = ["test_*.py"]
|