flowmesh-sdk-stack 0.1.2__tar.gz → 0.1.3__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 (19) hide show
  1. {flowmesh_sdk_stack-0.1.2/src/flowmesh_sdk_stack.egg-info → flowmesh_sdk_stack-0.1.3}/PKG-INFO +2 -2
  2. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/pyproject.toml +2 -2
  3. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3/src/flowmesh_sdk_stack.egg-info}/PKG-INFO +2 -2
  4. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_sdk_stack.egg-info/requires.txt +1 -1
  5. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/workers.py +93 -41
  6. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/LICENSE +0 -0
  7. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/README.md +0 -0
  8. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/setup.cfg +0 -0
  9. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_sdk_stack.egg-info/SOURCES.txt +0 -0
  10. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_sdk_stack.egg-info/dependency_links.txt +0 -0
  11. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_sdk_stack.egg-info/top_level.txt +0 -0
  12. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/__init__.py +0 -0
  13. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/docker.py +0 -0
  14. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/doctor.py +0 -0
  15. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/env.py +0 -0
  16. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/env_schema.py +0 -0
  17. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/images.py +0 -0
  18. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/node_client.py +0 -0
  19. {flowmesh_sdk_stack-0.1.2 → flowmesh_sdk_stack-0.1.3}/src/flowmesh_stack/paths.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowmesh-sdk-stack
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: FlowMesh SDK stack client
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: flowmesh-sdk==0.1.2
9
+ Requires-Dist: flowmesh-sdk==0.1.3
10
10
  Requires-Dist: httpx>=0.27.0
11
11
  Requires-Dist: pyyaml>=6.0.0
12
12
  Requires-Dist: docker>=7.1.0
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flowmesh-sdk-stack"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "FlowMesh SDK stack client"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
11
  license = "Apache-2.0"
12
12
  license-files = ["LICENSE"]
13
13
  dependencies = [
14
- "flowmesh-sdk==0.1.2",
14
+ "flowmesh-sdk==0.1.3",
15
15
  "httpx>=0.27.0",
16
16
  "pyyaml>=6.0.0",
17
17
  "docker>=7.1.0",
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowmesh-sdk-stack
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: FlowMesh SDK stack client
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: flowmesh-sdk==0.1.2
9
+ Requires-Dist: flowmesh-sdk==0.1.3
10
10
  Requires-Dist: httpx>=0.27.0
11
11
  Requires-Dist: pyyaml>=6.0.0
12
12
  Requires-Dist: docker>=7.1.0
@@ -1,4 +1,4 @@
1
- flowmesh-sdk==0.1.2
1
+ flowmesh-sdk==0.1.3
2
2
  httpx>=0.27.0
3
3
  pyyaml>=6.0.0
4
4
  docker>=7.1.0
@@ -65,12 +65,20 @@ def create_workers(
65
65
  targets: str = "all",
66
66
  config_paths: list[Path] | None = None,
67
67
  config_raw: list[str] | None = None,
68
+ name_template: str | None = None,
69
+ slug: str | None = None,
68
70
  ) -> list[tuple[str, dict[str, Any]]]:
69
71
  """Create node workers from configs or built-in cpu/gpu presets.
70
72
 
71
73
  When ``kind`` is "gpu", if ``count`` is equal to 1, a single worker with the
72
74
  specified GPU targets will be created. If ``count`` is greater than 1, one worker
73
75
  will be created per GPU target, and the number of targets must match ``count``.
76
+
77
+ For the built-in cpu/gpu presets, ``slug`` prefixes the generated worker
78
+ aliases so names are scoped to a stack, and ``name_template`` overrides the
79
+ naming entirely (placeholders ``{slug}``, ``{kind}``, ``{idx}``, ``{gpu}``).
80
+ Both are ignored when ``config_paths`` / ``config_raw`` are provided, since
81
+ those payloads carry their own aliases.
74
82
  """
75
83
  payloads = _payloads_for_worker_create(
76
84
  kind=kind,
@@ -78,6 +86,8 @@ def create_workers(
78
86
  targets=targets,
79
87
  config_paths=config_paths,
80
88
  config_raw=config_raw,
89
+ name_template=name_template,
90
+ slug=slug,
81
91
  )
82
92
  if not payloads:
83
93
  return []
@@ -162,12 +172,47 @@ def detect_gpu_targets(targets: str) -> list[str]:
162
172
  return [item.strip() for item in result.stdout.splitlines() if item.strip()]
163
173
 
164
174
 
175
+ _ALIAS_FIELDS = "{slug}, {kind}, {idx}, {gpu}"
176
+
177
+
178
+ class _StrictFormatDict(dict[str, Any]):
179
+ def __missing__(self, key: str) -> Any:
180
+ raise KeyError(key)
181
+
182
+
183
+ def _worker_alias(
184
+ kind: str,
185
+ idx: int,
186
+ slug: str | None,
187
+ template: str | None,
188
+ gpu: str | None = None,
189
+ ) -> str:
190
+ if template is None:
191
+ template = (
192
+ ("{slug}_" if slug else "")
193
+ + f"worker_{kind}_"
194
+ + ("{idx}" if gpu is None else "{gpu}")
195
+ )
196
+ fields: dict[str, Any] = {"slug": slug or "", "kind": kind, "idx": idx}
197
+ if gpu is not None:
198
+ fields["gpu"] = gpu
199
+ try:
200
+ return template.format_map(_StrictFormatDict(fields))
201
+ except (KeyError, IndexError, ValueError) as exc:
202
+ raise FlowMeshError(
203
+ f"invalid --name-template ({exc}); "
204
+ f"available placeholders: {_ALIAS_FIELDS}"
205
+ ) from exc
206
+
207
+
165
208
  def _payloads_for_worker_create(
166
209
  kind: str,
167
210
  count: int,
168
211
  targets: str,
169
212
  config_paths: list[Path] | None,
170
213
  config_raw: list[str] | None,
214
+ name_template: str | None = None,
215
+ slug: str | None = None,
171
216
  ) -> list[tuple[str, str]]:
172
217
  if count < 1:
173
218
  raise FlowMeshError("Worker count must be at least 1.")
@@ -183,25 +228,17 @@ def _payloads_for_worker_create(
183
228
  _extend_payloads(payloads, f"worker from raw#{idx}", raw)
184
229
  return payloads
185
230
 
231
+ specs: list[tuple[str, dict[str, Any], str]] # alias, worker_config, label
186
232
  if kind == "cpu":
187
- return [
233
+ specs = [
188
234
  (
189
- json.dumps(
190
- {
191
- "provider": "docker",
192
- "init_on_start": True,
193
- "worker_config": {
194
- "worker_type": "cpu",
195
- "worker_alias": f"worker_cpu_{idx}",
196
- },
197
- }
198
- ),
235
+ (alias := _worker_alias("cpu", idx, slug, name_template)),
236
+ {"worker_type": "cpu", "worker_alias": alias},
199
237
  "CPU worker",
200
238
  )
201
239
  for idx in range(count)
202
240
  ]
203
-
204
- if kind == "gpu":
241
+ elif kind == "gpu":
205
242
  raw_gpu_ids = detect_gpu_targets(targets)
206
243
  gpu_ids: list[int] = []
207
244
  for raw_gpu_id in raw_gpu_ids:
@@ -217,47 +254,62 @@ def _payloads_for_worker_create(
217
254
  f"Consider setting count={len(gpu_ids)} or specifying exactly "
218
255
  f"{count} GPU targets."
219
256
  )
220
- gpu_payloads = [
257
+ specs = [
221
258
  (
222
- json.dumps(
223
- {
224
- "provider": "docker",
225
- "init_on_start": True,
226
- "worker_config": {
227
- "worker_type": "gpu",
228
- "cuda_devices": [gpu_id],
229
- "worker_alias": f"worker_gpu_{gpu_id}",
230
- },
231
- }
259
+ (
260
+ alias := _worker_alias(
261
+ "gpu", idx, slug, name_template, gpu=str(gpu_id)
262
+ )
232
263
  ),
264
+ {
265
+ "worker_type": "gpu",
266
+ "cuda_devices": [gpu_id],
267
+ "worker_alias": alias,
268
+ },
233
269
  f"GPU worker for GPU {gpu_id}",
234
270
  )
235
- for gpu_id in gpu_ids
271
+ for idx, gpu_id in enumerate(gpu_ids)
236
272
  ]
237
273
  else:
238
274
  worker_suffix = "all" if targets == "all" else "_".join(raw_gpu_ids)
239
- gpu_payloads = [
275
+ specs = [
240
276
  (
241
- json.dumps(
242
- {
243
- "provider": "docker",
244
- "init_on_start": True,
245
- "worker_config": {
246
- "worker_type": "gpu",
247
- "cuda_devices": gpu_ids,
248
- "worker_alias": f"worker_gpu_{worker_suffix}",
249
- },
250
- }
277
+ (
278
+ alias := _worker_alias(
279
+ "gpu", 0, slug, name_template, gpu=worker_suffix
280
+ )
251
281
  ),
282
+ {
283
+ "worker_type": "gpu",
284
+ "cuda_devices": gpu_ids,
285
+ "worker_alias": alias,
286
+ },
252
287
  f"GPU worker for GPUs {', '.join(raw_gpu_ids)}",
253
288
  )
254
289
  ]
290
+ else:
291
+ raise FlowMeshError("worker up expects kind cpu|gpu or use --config")
292
+
293
+ aliases = [alias for alias, _, _ in specs]
294
+ if len(set(aliases)) != len(aliases):
295
+ raise FlowMeshError(
296
+ "--name-template produced duplicate worker names; "
297
+ "include {idx} or {gpu} to disambiguate"
298
+ )
255
299
 
256
- if not gpu_payloads:
257
- raise FlowMeshError("No GPUs detected or specified.")
258
- return gpu_payloads
259
-
260
- raise FlowMeshError("worker up expects kind cpu|gpu or use --config")
300
+ return [
301
+ (
302
+ json.dumps(
303
+ {
304
+ "provider": "docker",
305
+ "init_on_start": True,
306
+ "worker_config": worker_config,
307
+ }
308
+ ),
309
+ label,
310
+ )
311
+ for _, worker_config, label in specs
312
+ ]
261
313
 
262
314
 
263
315
  def _extend_payloads(