flashruntime 0.3.0__py3-none-any.whl

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 (95) hide show
  1. flashml_workloads/__init__.py +7 -0
  2. flashml_workloads/fedavg_driver.py +569 -0
  3. flashml_workloads/fedavg_weights.py +223 -0
  4. flashml_workloads/fedavg_worker.py +166 -0
  5. flashml_workloads/kmeans_driver.py +134 -0
  6. flashml_workloads/kmeans_shard.py +69 -0
  7. flashml_workloads/sgd_trainer.py +127 -0
  8. flashml_workloads/sharded_kmeans.py +323 -0
  9. flashml_workloads/sklearn_trial.py +89 -0
  10. flashruntime/__init__.py +125 -0
  11. flashruntime/artifacts/__init__.py +25 -0
  12. flashruntime/artifacts/store.py +228 -0
  13. flashruntime/backends/__init__.py +26 -0
  14. flashruntime/backends/base.py +63 -0
  15. flashruntime/backends/kuberay.py +465 -0
  16. flashruntime/checkpoint/__init__.py +20 -0
  17. flashruntime/checkpoint/catalog.py +198 -0
  18. flashruntime/checkpoint/local.py +109 -0
  19. flashruntime/checkpoint/store.py +86 -0
  20. flashruntime/integrations/__init__.py +5 -0
  21. flashruntime/integrations/huggingface.py +59 -0
  22. flashruntime/integrations/pytorch.py +52 -0
  23. flashruntime/integrations/sklearn.py +42 -0
  24. flashruntime/launchers/__init__.py +130 -0
  25. flashruntime/launchers/local.py +126 -0
  26. flashruntime/leases/__init__.py +27 -0
  27. flashruntime/leases/manager.py +365 -0
  28. flashruntime/leases/sqlite_store.py +169 -0
  29. flashruntime/leases/store.py +103 -0
  30. flashruntime/monitor/__init__.py +7 -0
  31. flashruntime/monitor/sampler.py +232 -0
  32. flashruntime/planner/__init__.py +56 -0
  33. flashruntime/planner/candidates.py +597 -0
  34. flashruntime/planner/catalog.py +129 -0
  35. flashruntime/planner/comm.py +95 -0
  36. flashruntime/planner/explain.py +109 -0
  37. flashruntime/planner/memory.py +166 -0
  38. flashruntime/planner/resolve.py +120 -0
  39. flashruntime/planner/selector.py +169 -0
  40. flashruntime/planner/timecost.py +81 -0
  41. flashruntime/profiling/__init__.py +113 -0
  42. flashruntime/protocol/__init__.py +18 -0
  43. flashruntime/protocol/plan_v1alpha1.py +320 -0
  44. flashruntime/protocol/v1alpha1.py +465 -0
  45. flashruntime/providers/__init__.py +138 -0
  46. flashruntime/py.typed +0 -0
  47. flashruntime/recipes/__init__.py +135 -0
  48. flashruntime/recipes/command.py +166 -0
  49. flashruntime/recovery/__init__.py +21 -0
  50. flashruntime/recovery/policy.py +170 -0
  51. flashruntime/recovery/signals.py +135 -0
  52. flashruntime/recovery/taxonomy.py +91 -0
  53. flashruntime/scheduler/__init__.py +170 -0
  54. flashruntime/sdk.py +402 -0
  55. flashruntime/service/__init__.py +3 -0
  56. flashruntime/service/app.py +391 -0
  57. flashruntime/service/auth.py +180 -0
  58. flashruntime/service/checkpoints.py +90 -0
  59. flashruntime/service/cli.py +167 -0
  60. flashruntime/service/dashboard.py +193 -0
  61. flashruntime/service/ledger.py +101 -0
  62. flashruntime/service/modea.py +821 -0
  63. flashruntime/strategies/__init__.py +156 -0
  64. flashruntime/strategies/command.py +56 -0
  65. flashruntime/torch/__init__.py +274 -0
  66. flashruntime/viewer/__init__.py +20 -0
  67. flashruntime/viewer/_docs/benchmarks.html +771 -0
  68. flashruntime/viewer/_docs/concepts/architecture.html +302 -0
  69. flashruntime/viewer/_docs/get-started.html +263 -0
  70. flashruntime/viewer/_docs/guides/federated-averaging.html +363 -0
  71. flashruntime/viewer/_docs/guides/huggingface.html +223 -0
  72. flashruntime/viewer/_docs/guides/jobspec-and-isolation.html +271 -0
  73. flashruntime/viewer/_docs/guides/pytorch.html +313 -0
  74. flashruntime/viewer/_docs/guides/sklearn.html +232 -0
  75. flashruntime/viewer/_docs/index.html +251 -0
  76. flashruntime/viewer/_docs/reference/cli.html +254 -0
  77. flashruntime/viewer/_docs/reference/integrations.html +240 -0
  78. flashruntime/viewer/_docs/reference/sdk.html +341 -0
  79. flashruntime/viewer/_docs/reference/torch-helper.html +244 -0
  80. flashruntime/viewer/_docs/search-index.json +1 -0
  81. flashruntime/viewer/_docs/tutorials/convnet.html +571 -0
  82. flashruntime/viewer/_docs/tutorials/fault-tolerance.html +375 -0
  83. flashruntime/viewer/_docs/tutorials/sklearn-sweeps.html +278 -0
  84. flashruntime/viewer/flowmap.py +307 -0
  85. flashruntime/viewer/page.py +594 -0
  86. flashruntime/viewer/server.py +134 -0
  87. flashruntime/viewer/state.py +250 -0
  88. flashruntime/workloads/__init__.py +6 -0
  89. flashruntime/workloads/command.py +127 -0
  90. flashruntime-0.3.0.dist-info/METADATA +365 -0
  91. flashruntime-0.3.0.dist-info/RECORD +95 -0
  92. flashruntime-0.3.0.dist-info/WHEEL +5 -0
  93. flashruntime-0.3.0.dist-info/entry_points.txt +2 -0
  94. flashruntime-0.3.0.dist-info/licenses/LICENSE +202 -0
  95. flashruntime-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,320 @@
1
+ """FlashML planning protocol, version v1alpha1.
2
+
3
+ Wire-visible models for the strategy planner: what a user *asks for*
4
+ (`PlanRequest` = workload + resources + objective) and what the planner
5
+ *answers* (`PlanReport` = a selected `StrategyPlan` plus every candidate
6
+ verdict with its arithmetic). These schemas are the contract between the
7
+ SDK/CLI, FlashML Cloud, and — later — the execution layer that compiles a
8
+ `StrategyPlan` into a running job.
9
+
10
+ Design rules (ADR-0003):
11
+ - Backend-neutral: nothing here requires importing torch/ray/transformers to
12
+ parse. Strategy families and launchers are *names*; strategy compilers
13
+ translate them into real configuration at execution time.
14
+ - Every numeric estimate carries a `basis` (`static` | `profiled` | `ledger`)
15
+ so consumers can tell arithmetic from measurement.
16
+ - Explanations — including *rejected* candidates — are part of the contract,
17
+ not decoration.
18
+ - Additive changes only within v1alpha1; breaking changes go to a new module.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from typing import Annotated, Literal, Union
24
+
25
+ from pydantic import BaseModel, Field
26
+
27
+ PLAN_API_VERSION = "flashml.dev/v1alpha1"
28
+
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Workloads — what the user wants to run.
32
+ #
33
+ # Classification is structural (what the computation looks like), never
34
+ # label-based: "LoRA" says which parameters train, not which distributed
35
+ # strategy to use. Each workload kind carries exactly the fields the
36
+ # estimators need.
37
+ # ---------------------------------------------------------------------------
38
+
39
+
40
+ class TransformerFineTune(BaseModel):
41
+ """Fine-tune a transformer language model (full, LoRA, or QLoRA).
42
+
43
+ `parameters_b` may be omitted for models in the planner's small catalog
44
+ (e.g. "Qwen/Qwen2.5-7B"); otherwise it is required. `hidden_size` /
45
+ `num_layers` improve the activation estimate; when absent the planner
46
+ derives a typical shape from the parameter count and labels the estimate
47
+ accordingly.
48
+ """
49
+
50
+ kind: Literal["transformer_finetune"] = "transformer_finetune"
51
+ model: str = Field(description="Model name, e.g. 'Qwen/Qwen2.5-7B'")
52
+ parameters_b: float | None = Field(
53
+ default=None, gt=0, description="Total parameters in billions"
54
+ )
55
+ method: Literal["full", "lora", "qlora"] = "lora"
56
+ lora_rank: int = Field(default=16, ge=1, le=256)
57
+ precision: Literal["bf16", "fp16", "fp32"] = "bf16"
58
+ optimizer: Literal["adamw", "adamw_8bit", "sgd_momentum"] = "adamw"
59
+ seq_len: int = Field(default=2048, ge=64)
60
+ micro_batch_per_gpu: int = Field(default=1, ge=1)
61
+ grad_accum: int = Field(default=8, ge=1)
62
+ activation_checkpointing: bool | None = Field(
63
+ default=None, description="None = planner decides"
64
+ )
65
+ hidden_size: int | None = Field(default=None, ge=64)
66
+ num_layers: int | None = Field(default=None, ge=1)
67
+ train_tokens_m: float | None = Field(
68
+ default=None,
69
+ gt=0,
70
+ description="Training set size in millions of tokens; enables time/cost/deadline estimates",
71
+ )
72
+
73
+
74
+ class PyTorchTraining(BaseModel):
75
+ """Generic PyTorch deep-learning training (CNNs, GNNs, custom models).
76
+
77
+ Without a transformer's regular shape the planner cannot derive
78
+ activation memory structurally, so the user supplies (or accepts a
79
+ pessimistic default for) `activation_gb_per_gpu`.
80
+ """
81
+
82
+ kind: Literal["pytorch_training"] = "pytorch_training"
83
+ model: str = Field(default="custom", description="Informational model name")
84
+ parameters_m: float = Field(gt=0, description="Total parameters in millions")
85
+ trainable_fraction: float = Field(default=1.0, gt=0, le=1.0)
86
+ precision: Literal["bf16", "fp16", "fp32"] = "fp32"
87
+ optimizer: Literal["adamw", "adamw_8bit", "sgd_momentum"] = "adamw"
88
+ activation_gb_per_gpu: float | None = Field(
89
+ default=None,
90
+ gt=0,
91
+ description="Measured/estimated activation memory per GPU; default is a pessimistic assumption",
92
+ )
93
+ est_train_gpu_hours: float | None = Field(
94
+ default=None, gt=0, description="Single-GPU wall-clock estimate; enables time/cost"
95
+ )
96
+
97
+
98
+ class ClassicalML(BaseModel):
99
+ """Classical machine learning (scikit-learn / XGBoost style)."""
100
+
101
+ kind: Literal["classical_ml"] = "classical_ml"
102
+ library: Literal["sklearn", "xgboost"] = "sklearn"
103
+ algorithm: str = Field(default="unspecified", description="e.g. 'kmeans', 'random_forest'")
104
+ dataset_mb: float = Field(gt=0, description="In-memory dataset size in MB")
105
+ supports_partial_fit: bool = Field(
106
+ default=False,
107
+ description="True if the estimator can learn incrementally (enables sharded Mode A execution)",
108
+ )
109
+
110
+
111
+ class IndependentTasks(BaseModel):
112
+ """A set of tasks with no communication between them (Mode A).
113
+
114
+ Hyperparameter search, cross-validation folds, batch inference shards,
115
+ per-seed synthetic-data generation, evaluation suites.
116
+ """
117
+
118
+ kind: Literal["independent_tasks"] = "independent_tasks"
119
+ task_kind: Literal[
120
+ "hyperparameter_search", "batch_inference", "evaluation", "preprocessing", "other"
121
+ ] = "hyperparameter_search"
122
+ task_count: int = Field(ge=1)
123
+ est_minutes_per_task: float = Field(default=10.0, gt=0)
124
+ needs_gpu: bool = False
125
+ vram_gb_per_task: float = Field(default=0.0, ge=0)
126
+
127
+
128
+ Workload = Annotated[
129
+ Union[TransformerFineTune, PyTorchTraining, ClassicalML, IndependentTasks],
130
+ Field(discriminator="kind"),
131
+ ]
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # Resources and objective — what the user has, and what they care about.
136
+ # ---------------------------------------------------------------------------
137
+
138
+ Interconnect = Literal[
139
+ "same_host_nvlink", # NVLink/NVSwitch inside one machine
140
+ "same_host_pcie", # multiple GPUs on one machine over PCIe
141
+ "multi_node_ib", # InfiniBand / RoCE between machines
142
+ "multi_node_100g", # 100 Gb/s Ethernet
143
+ "multi_node_10g", # 10 Gb/s Ethernet
144
+ "wan", # internet-grade links (community devices)
145
+ ]
146
+
147
+
148
+ class Resources(BaseModel):
149
+ """The compute the plan may use. GPU-count 0 means CPU-only."""
150
+
151
+ gpus: int = Field(default=0, ge=0)
152
+ gpu_type: str | None = Field(
153
+ default=None, description="e.g. 'A100-40GB', 'L40S', 'RTX4090'; keys the GPU catalog"
154
+ )
155
+ vram_gb: float | None = Field(
156
+ default=None, gt=0, description="Per-GPU VRAM; overrides the catalog value"
157
+ )
158
+ hosts: int = Field(default=1, ge=1, description="How many machines the GPUs are spread over")
159
+ interconnect: Interconnect = "same_host_pcie"
160
+ cpu_ram_gb: float = Field(default=32.0, gt=0, description="Host RAM per machine")
161
+ cpu_cores: int = Field(default=8, ge=1)
162
+ hourly_cost_usd_per_gpu: float | None = Field(default=None, ge=0)
163
+
164
+
165
+ class Objective(BaseModel):
166
+ """Constraints and preference. Hard constraints reject plans; the mode
167
+ orders the survivors."""
168
+
169
+ mode: Literal["cheapest", "fastest", "balanced", "reliable"] = "balanced"
170
+ max_cost_usd: float | None = Field(default=None, gt=0)
171
+ deadline_minutes: float | None = Field(default=None, gt=0)
172
+ allow_quantization: bool = True
173
+ allow_cpu_offload: bool = True
174
+ allow_nvme_offload: bool = False
175
+
176
+
177
+ class PlanRequest(BaseModel):
178
+ """The planner's input: intent + resources + objective."""
179
+
180
+ apiVersion: str = PLAN_API_VERSION
181
+ kind: Literal["PlanRequest"] = "PlanRequest"
182
+ workload: Workload
183
+ resources: Resources = Field(default_factory=Resources)
184
+ objective: Objective = Field(default_factory=Objective)
185
+
186
+
187
+ # ---------------------------------------------------------------------------
188
+ # Planner output
189
+ # ---------------------------------------------------------------------------
190
+
191
+ EstimateBasis = Literal["static", "profiled", "ledger"]
192
+
193
+
194
+ class Estimate(BaseModel):
195
+ """A number that admits where it came from."""
196
+
197
+ value: float
198
+ unit: str
199
+ basis: EstimateBasis = "static"
200
+ note: str = ""
201
+
202
+
203
+ class MemoryBreakdown(BaseModel):
204
+ """Per-GPU peak memory estimate in GB (1 GB = 1e9 bytes), by component.
205
+
206
+ `transient_gb` covers FSDP/ZeRO-3 per-layer all-gather peaks;
207
+ `cpu_offload_gb` is host RAM claimed by offloaded state (checked against
208
+ `Resources.cpu_ram_gb` — planners that only check VRAM kill hosts).
209
+ """
210
+
211
+ weights_gb: float = 0.0
212
+ gradients_gb: float = 0.0
213
+ optimizer_gb: float = 0.0
214
+ activations_gb: float = 0.0
215
+ transient_gb: float = 0.0
216
+ overhead_gb: float = 0.0
217
+ total_gb: float = 0.0
218
+ cpu_offload_gb: float = 0.0
219
+ basis: EstimateBasis = "static"
220
+
221
+
222
+ class LibraryRef(BaseModel):
223
+ """One library the selected strategy is built on, and its role."""
224
+
225
+ name: str
226
+ role: str # e.g. "launcher", "strategy", "workload", "checkpoint", "runtime"
227
+ purpose: str = ""
228
+
229
+
230
+ CandidateStatus = Literal[
231
+ "selected", # the winner
232
+ "feasible", # would work; lost the ranking
233
+ "infeasible", # fails a physical check (memory, network)
234
+ "rejected_policy", # violates a user constraint (budget, deadline, allow_* flag)
235
+ "rejected_dominated", # feasible but strictly worse than another candidate
236
+ ]
237
+
238
+
239
+ class CandidateVerdict(BaseModel):
240
+ """One evaluated candidate, with its numbers and its fate.
241
+
242
+ Rejections are half the product: a user who sees *why* DDP was infeasible
243
+ trusts the plan that was chosen.
244
+ """
245
+
246
+ name: str
247
+ strategy_family: str
248
+ workers: int
249
+ status: CandidateStatus
250
+ memory: MemoryBreakdown | None = None
251
+ est_time_min: Estimate | None = None
252
+ est_cost_usd: Estimate | None = None
253
+ scaling_efficiency: float | None = None
254
+ profiling_required: bool = False
255
+ reasons: list[str] = Field(default_factory=list)
256
+
257
+
258
+ class CheckpointPolicy(BaseModel):
259
+ backend: str = "pytorch_dcp"
260
+ interval_seconds: int = 300
261
+ note: str = ""
262
+
263
+
264
+ class StrategyPlan(BaseModel):
265
+ """The planner's answer: a frozen, backend-neutral execution plan.
266
+
267
+ Frozen means: `plan_id` is a content hash of the request + decisions +
268
+ planner version, recorded on the job attempt at execution time so every
269
+ incident can answer "what did we think, and why".
270
+ """
271
+
272
+ apiVersion: str = PLAN_API_VERSION
273
+ kind: Literal["StrategyPlan"] = "StrategyPlan"
274
+ plan_id: str
275
+ planner_version: str
276
+
277
+ workload_mode: Literal["local", "independent_tasks", "coordinated_training"]
278
+ strategy_family: str # e.g. "ddp", "fsdp2", "zero3_cpu_offload", "lease_tasks", "local_process"
279
+ launcher: str # e.g. "torchrun", "flashruntime-leases", "local"
280
+
281
+ workers: int = 1
282
+ gpus_per_worker: int = 0
283
+ colocated: bool = True
284
+
285
+ precision: str | None = None
286
+ quantization: str | None = None # e.g. "nf4" for QLoRA frozen weights
287
+ peft: str | None = None # e.g. "lora(r=16)"
288
+ micro_batch_per_gpu: int | None = None
289
+ grad_accum: int | None = None
290
+ activation_checkpointing: bool | None = None
291
+ offload: Literal["none", "optimizer_cpu", "params_cpu", "nvme"] = "none"
292
+
293
+ libraries: list[LibraryRef] = Field(default_factory=list)
294
+ checkpoint: CheckpointPolicy | None = None
295
+
296
+ memory: MemoryBreakdown | None = None
297
+ est_time_min: Estimate | None = None
298
+ est_cost_usd: Estimate | None = None
299
+ scaling_efficiency: float | None = None
300
+ profiling_required: bool = False
301
+
302
+ selected_because: list[str] = Field(default_factory=list)
303
+
304
+
305
+ class PlanReport(BaseModel):
306
+ """Everything the planner concluded: the winner and the full field.
307
+
308
+ `selected` is None when no candidate survived; `no_valid_strategy_hint`
309
+ then carries the nearest-miss analysis (the minimal relaxation that would
310
+ unlock a plan) — a dead end must still be a useful answer.
311
+ """
312
+
313
+ apiVersion: str = PLAN_API_VERSION
314
+ kind: Literal["PlanReport"] = "PlanReport"
315
+ planner_version: str
316
+ request_digest: str
317
+ selected: StrategyPlan | None = None
318
+ candidates: list[CandidateVerdict] = Field(default_factory=list)
319
+ warnings: list[str] = Field(default_factory=list)
320
+ no_valid_strategy_hint: str | None = None