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,254 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Reference: CLI (`flashruntime`) — flashruntime docs</title>
7
+ <style>
8
+ :root { color-scheme: dark; }
9
+ * { box-sizing: border-box; margin: 0; }
10
+ body { font: 14px/1.65 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: #0d1117; color: #c9d1d9; }
11
+ a { color: oklch(0.80 0.16 200); text-decoration: none; }
12
+ a:hover { text-decoration: underline; }
13
+ .layout { display: flex; align-items: flex-start; min-height: 100vh; }
14
+
15
+ /* sidebar — PyTorch-docs-like: sections from _nav.yml, current page active */
16
+ .sidebar { flex: 0 0 264px; position: sticky; top: 0; height: 100vh; overflow-y: auto;
17
+ border-right: 1px solid #21262d; background: #0d1117; padding: 20px 16px; }
18
+ .brand { color: #e6edf3; font-size: 15px; letter-spacing: .04em; }
19
+ .brand small { display: block; margin-top: 2px; color: #8b949e; font-size: 10px;
20
+ text-transform: uppercase; letter-spacing: .14em; }
21
+ .search { position: relative; margin: 16px 0; }
22
+ #q { width: 100%; background: #010409; border: 1px solid #21262d; border-radius: 6px;
23
+ color: #c9d1d9; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; padding: 7px 9px; }
24
+ #q:focus { outline: 1px solid oklch(0.80 0.16 200); }
25
+ #results { position: absolute; left: 0; right: 0; top: 112%; z-index: 5; display: none;
26
+ background: #161b22; border: 1px solid #21262d; border-radius: 6px; overflow: hidden; }
27
+ #results.open { display: block; }
28
+ #results a { display: block; padding: 7px 9px; border-bottom: 1px solid #21262d; color: #c9d1d9; }
29
+ #results a:last-child { border-bottom: 0; }
30
+ #results a:hover { background: #010409; text-decoration: none; }
31
+ #results a b { color: #e6edf3; font-weight: 600; }
32
+ #results a span { display: block; margin-top: 2px; color: #8b949e; font-size: 11px; }
33
+ #results .nohit { padding: 7px 9px; color: #8b949e; }
34
+ .nav-section { margin: 16px 0 6px; color: #8b949e; font-size: 10px; font-weight: 600;
35
+ text-transform: uppercase; letter-spacing: .14em; }
36
+ .nav-link { display: block; padding: 4px 8px; border-radius: 5px; color: #c9d1d9; font-size: 13px; }
37
+ .nav-link:hover { background: #161b22; text-decoration: none; }
38
+ .nav-link.active { background: #161b22; color: #e6edf3; box-shadow: inset 2px 0 0 oklch(0.80 0.16 200); }
39
+
40
+ /* content */
41
+ .content { flex: 1 1 auto; max-width: 840px; min-width: 0; padding: 34px 40px 80px; }
42
+ .content h1 { color: #e6edf3; font-size: 26px; margin: 0 0 16px; }
43
+ .content h2 { color: #e6edf3; font-size: 18px; margin: 30px 0 10px;
44
+ padding-top: 10px; border-top: 1px solid #21262d; }
45
+ .content h3 { color: #e6edf3; font-size: 15px; margin: 22px 0 8px; }
46
+ .content p, .content li { color: #c9d1d9; }
47
+ .content ul, .content ol { padding-left: 22px; margin: 10px 0; }
48
+ .content li { margin: 4px 0; }
49
+ .content blockquote { margin: 14px 0; padding: 2px 14px; border-left: 3px solid oklch(0.80 0.18 60);
50
+ background: #161b22; border-radius: 0 6px 6px 0; color: #8b949e; }
51
+ .content :not(pre) > code { background: #010409; border: 1px solid #21262d;
52
+ border-radius: 4px; padding: 1px 5px; font-size: 12.5px; }
53
+ .content table { border-collapse: collapse; margin: 14px 0; display: block; overflow-x: auto; }
54
+ .content th, .content td { border: 1px solid #21262d; padding: 6px 10px; text-align: left; }
55
+ .content th { color: #e6edf3; background: #161b22; }
56
+
57
+ /* code blocks + copy button */
58
+ .codeblock { position: relative; margin: 14px 0; }
59
+ .codeblock pre { background: #010409; border: 1px solid #21262d; border-radius: 8px;
60
+ padding: 14px 16px; overflow-x: auto; }
61
+ .codeblock pre code { padding: 0; border: 0; background: none; font-size: 12.5px; color: #c9d1d9; }
62
+ .copybtn { position: absolute; top: 8px; right: 8px; padding: 3px 8px; cursor: pointer;
63
+ background: #161b22; border: 1px solid #21262d; border-radius: 5px;
64
+ color: #8b949e; font: 11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
65
+ .copybtn:hover { color: #e6edf3; border-color: oklch(0.80 0.16 200); }
66
+
67
+ @media (max-width: 800px) {
68
+ .layout { flex-direction: column; }
69
+ .sidebar { position: static; height: auto; width: 100%; flex-basis: auto;
70
+ border-right: 0; border-bottom: 1px solid #21262d; }
71
+ .content { padding: 24px 18px 60px; }
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div class="layout">
77
+ <aside class="sidebar">
78
+ <a class="brand" href="../index.html">flashruntime<small>documentation</small></a>
79
+ <div class="search">
80
+ <input id="q" type="search" placeholder="Search docs (press /)" autocomplete="off" spellcheck="false">
81
+ <div id="results"></div>
82
+ </div>
83
+ <nav><div class="nav-section">Getting started</div>
84
+ <a class="nav-link" href="../index.html">FlashRuntime</a>
85
+ <a class="nav-link" href="../get-started.html">Get started</a>
86
+ <div class="nav-section">Tutorials</div>
87
+ <a class="nav-link" href="../tutorials/convnet.html">Tutorial: make a ConvNet fault-tolerant</a>
88
+ <a class="nav-link" href="../tutorials/sklearn-sweeps.html">Tutorial: parallel scikit-learn sweeps</a>
89
+ <a class="nav-link" href="../tutorials/fault-tolerance.html">Tutorial: automatic recovery, explained</a>
90
+ <div class="nav-section">Guides</div>
91
+ <a class="nav-link" href="../guides/pytorch.html">PyTorch guide</a>
92
+ <a class="nav-link" href="../guides/sklearn.html">scikit-learn guide</a>
93
+ <a class="nav-link" href="../guides/huggingface.html">Hugging Face guide</a>
94
+ <a class="nav-link" href="../guides/jobspec-and-isolation.html">JobSpec &amp; isolation guide</a>
95
+ <a class="nav-link" href="../guides/federated-averaging.html">Federated averaging</a>
96
+ <div class="nav-section">Concepts</div>
97
+ <a class="nav-link" href="../concepts/architecture.html">Architecture</a>
98
+ <div class="nav-section">Reference</div>
99
+ <a class="nav-link" href="sdk.html">Reference: SDK (`flashruntime`)</a>
100
+ <a class="nav-link" href="integrations.html">Reference: integrations (`flashruntime.integrations`)</a>
101
+ <a class="nav-link" href="torch-helper.html">Reference: torch helper (`flashruntime.torch`)</a>
102
+ <a class="nav-link active" href="cli.html">Reference: CLI (`flashruntime`)</a>
103
+ <div class="nav-section">Benchmarks</div>
104
+ <a class="nav-link" href="../benchmarks.html">Benchmarks</a></nav>
105
+ </aside>
106
+ <main class="content"><h1 id="reference-cli-flashruntime">Reference: CLI (<code>flashruntime</code>)</h1>
107
+ <p>The <code>flashruntime</code> command ships with the <code>[service]</code> extra
108
+ (<code>pip install "flashruntime[service]"</code>). It is the terminal front door to the
109
+ same operations the SDK exposes: plan a job offline, run a command workload
110
+ locally, and talk to a coordinator.</p>
111
+ <p>The blocks below mirror the real <code>--help</code> output.</p>
112
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code>usage: flashruntime [-h] [--api API]
113
+ {plan,submit,submit-spec,status,events,logs,cancel} ...
114
+
115
+ positional arguments:
116
+ {plan,submit,submit-spec,status,events,logs,cancel}
117
+ plan evaluate a PlanRequest offline and print the strategy
118
+ submit run a command workload locally (no API needed)
119
+ submit-spec POST a JobSpec YAML to the coordinator — was `submit`
120
+ before 0.1.0; renamed when `submit` became the local-
121
+ workload front door
122
+
123
+ options:
124
+ -h, --help show this help message and exit
125
+ --api API FlashRuntime API base URL
126
+ </code></pre></div>
127
+ <p><code>--api</code> names the coordinator base URL for the service-side subcommands
128
+ (<code>submit-spec</code>, <code>status</code>, <code>events</code>, <code>logs</code>, <code>cancel</code>).</p>
129
+ <hr />
130
+ <h2 id="plan-offline-strategy-selection"><code>plan</code> — offline strategy selection</h2>
131
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code>usage: flashruntime plan [-h] [--json] request_file
132
+
133
+ positional arguments:
134
+ request_file PlanRequest as .yaml or .json
135
+
136
+ options:
137
+ -h, --help show this help message and exit
138
+ --json emit the full PlanReport as JSON
139
+ </code></pre></div>
140
+ <p>Runs <code>flash.plan()</code> on a <code>PlanRequest</code> file and prints the explained strategy;
141
+ <code>--json</code> emits the full <code>PlanReport</code>. No cluster required.</p>
142
+ <hr />
143
+ <h2 id="submit-run-a-command-workload-locally"><code>submit</code> — run a command workload locally</h2>
144
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code>usage: flashruntime submit [-h] [--source SOURCE] [--task-params TASK_PARAMS]
145
+ [--max-restarts MAX_RESTARTS]
146
+ [--output-dir OUTPUT_DIR] [--watch | --no-watch]
147
+ CMD
148
+
149
+ positional arguments:
150
+ CMD the command to run, e.g. 'python train.py --lr {lr}'
151
+
152
+ options:
153
+ -h, --help show this help message and exit
154
+ --source SOURCE directory holding the user's code
155
+ --task-params TASK_PARAMS
156
+ JSON list of param dicts for Mode A fan-out
157
+ --max-restarts MAX_RESTARTS
158
+ automatic recovery budget
159
+ --output-dir OUTPUT_DIR
160
+ where run.json and artifacts land (default: temp dir)
161
+ --watch, --no-watch open the live viewer (default: on at a terminal, off
162
+ in pipes/CI)
163
+ </code></pre></div>
164
+ <p>The terminal equivalent of <code>flash.submit()</code>. <code>--task-params</code> (a JSON list of
165
+ param dicts) fills <code>{name}</code> placeholders in <code>CMD</code> for a fan-out sweep;
166
+ <code>--max-restarts</code> is the automatic fault-tolerance budget.</p>
167
+ <hr />
168
+ <h2 id="submit-spec-post-a-jobspec-to-the-coordinator"><code>submit-spec</code> — POST a JobSpec to the coordinator</h2>
169
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code>usage: flashruntime submit-spec [-h] spec_file
170
+
171
+ positional arguments:
172
+ spec_file
173
+
174
+ options:
175
+ -h, --help show this help message and exit
176
+ </code></pre></div>
177
+ <p>POSTs a <code>JobSpec</code> YAML/JSON file (e.g. one produced by
178
+ <code>workloads.command.to_jobspec</code>) to the coordinator named by <code>--api</code>. This was
179
+ called <code>submit</code> before 0.1.0 — it was renamed when local <code>submit</code> became the
180
+ default front door.</p>
181
+ <hr />
182
+ <h2 id="status-events-logs-cancel-inspect-a-coordinator-job"><code>status</code> / <code>events</code> / <code>logs</code> / <code>cancel</code> — inspect a coordinator job</h2>
183
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code>usage: flashruntime status [-h] job_id
184
+ usage: flashruntime events [-h] job_id
185
+ usage: flashruntime logs [-h] job_id
186
+ usage: flashruntime cancel [-h] job_id
187
+ </code></pre></div>
188
+ <p>Each takes a <code>job_id</code> and queries the coordinator at <code>--api</code>: <code>status</code> for the
189
+ derived job state, <code>events</code> for the append-only ledger, <code>logs</code> for captured
190
+ output, and <code>cancel</code> to stop a job.</p>
191
+ <hr />
192
+ <p>See the <a href="../guides/jobspec-and-isolation.html">JobSpec &amp; isolation guide</a> for the
193
+ wire form these service subcommands operate on, and the <a href="sdk.html">SDK reference</a>
194
+ for the in-process equivalents.</p></main>
195
+ </div>
196
+
197
+ <script>
198
+ // ---- client-side search: fetch the builder's index, filter as you type -----
199
+ // (<=60 lines, vanilla JS, no external anything — the index is a sibling file.)
200
+ let INDEX = [];
201
+ // Path from THIS page back to the site root. The index stores root-relative
202
+ // URLs (e.g. "tutorials/deep.html"); we fetch it and build result links
203
+ // against ROOT so a page in a subdir resolves them too (see build_docs.py).
204
+ const ROOT = "../";
205
+ const q = document.getElementById("q");
206
+ const results = document.getElementById("results");
207
+ const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
208
+ ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
209
+ fetch(ROOT + "search-index.json").then((r) => r.json()).then((d) => { INDEX = d; }).catch(() => {});
210
+
211
+ // A short context window around the first match, so a hit shows WHY it matched.
212
+ function snippet(text, needle) {
213
+ const i = text.toLowerCase().indexOf(needle);
214
+ if (i < 0) return "";
215
+ const start = Math.max(0, i - 32);
216
+ return (start > 0 ? "…" : "") + text.slice(start, i + needle.length + 44).trim() + "…";
217
+ }
218
+ function runSearch() {
219
+ const needle = q.value.trim().toLowerCase();
220
+ if (!needle) { results.className = ""; results.innerHTML = ""; return; }
221
+ const hits = INDEX.map((p) => {
222
+ const inTitle = p.title.toLowerCase().includes(needle);
223
+ const inText = p.text.toLowerCase().includes(needle);
224
+ if (!inTitle && !inText) return null;
225
+ return { url: p.url, title: p.title, snip: inText ? snippet(p.text, needle) : "" };
226
+ }).filter(Boolean).slice(0, 20);
227
+ results.className = "open";
228
+ results.innerHTML = hits.length
229
+ ? hits.map((h) => '<a href="' + ROOT + h.url + '"><b>' + esc(h.title) + "</b>" +
230
+ (h.snip ? "<span>" + esc(h.snip) + "</span>" : "") + "</a>").join("")
231
+ : '<div class="nohit">no matches</div>';
232
+ }
233
+ q.addEventListener("input", runSearch);
234
+ q.addEventListener("focus", runSearch);
235
+ document.addEventListener("click", (e) => {
236
+ if (!e.target.closest(".search")) { results.className = ""; } // dismiss on outside click
237
+ });
238
+ document.addEventListener("keydown", (e) => { // "/" focuses search, like PyTorch docs
239
+ if (e.key === "/" && document.activeElement !== q) { e.preventDefault(); q.focus(); }
240
+ });
241
+
242
+ // ---- copy buttons: read the <pre>'s own text into the clipboard ------------
243
+ document.querySelectorAll(".copybtn").forEach((btn) => {
244
+ btn.addEventListener("click", () => {
245
+ const code = btn.parentElement.querySelector("pre").innerText;
246
+ navigator.clipboard.writeText(code).then(() => {
247
+ const was = btn.textContent; btn.textContent = "Copied";
248
+ setTimeout(() => { btn.textContent = was; }, 1200);
249
+ }).catch(() => {});
250
+ });
251
+ });
252
+ </script>
253
+ </body>
254
+ </html>
@@ -0,0 +1,240 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Reference: integrations (`flashruntime.integrations`) — flashruntime docs</title>
7
+ <style>
8
+ :root { color-scheme: dark; }
9
+ * { box-sizing: border-box; margin: 0; }
10
+ body { font: 14px/1.65 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: #0d1117; color: #c9d1d9; }
11
+ a { color: oklch(0.80 0.16 200); text-decoration: none; }
12
+ a:hover { text-decoration: underline; }
13
+ .layout { display: flex; align-items: flex-start; min-height: 100vh; }
14
+
15
+ /* sidebar — PyTorch-docs-like: sections from _nav.yml, current page active */
16
+ .sidebar { flex: 0 0 264px; position: sticky; top: 0; height: 100vh; overflow-y: auto;
17
+ border-right: 1px solid #21262d; background: #0d1117; padding: 20px 16px; }
18
+ .brand { color: #e6edf3; font-size: 15px; letter-spacing: .04em; }
19
+ .brand small { display: block; margin-top: 2px; color: #8b949e; font-size: 10px;
20
+ text-transform: uppercase; letter-spacing: .14em; }
21
+ .search { position: relative; margin: 16px 0; }
22
+ #q { width: 100%; background: #010409; border: 1px solid #21262d; border-radius: 6px;
23
+ color: #c9d1d9; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; padding: 7px 9px; }
24
+ #q:focus { outline: 1px solid oklch(0.80 0.16 200); }
25
+ #results { position: absolute; left: 0; right: 0; top: 112%; z-index: 5; display: none;
26
+ background: #161b22; border: 1px solid #21262d; border-radius: 6px; overflow: hidden; }
27
+ #results.open { display: block; }
28
+ #results a { display: block; padding: 7px 9px; border-bottom: 1px solid #21262d; color: #c9d1d9; }
29
+ #results a:last-child { border-bottom: 0; }
30
+ #results a:hover { background: #010409; text-decoration: none; }
31
+ #results a b { color: #e6edf3; font-weight: 600; }
32
+ #results a span { display: block; margin-top: 2px; color: #8b949e; font-size: 11px; }
33
+ #results .nohit { padding: 7px 9px; color: #8b949e; }
34
+ .nav-section { margin: 16px 0 6px; color: #8b949e; font-size: 10px; font-weight: 600;
35
+ text-transform: uppercase; letter-spacing: .14em; }
36
+ .nav-link { display: block; padding: 4px 8px; border-radius: 5px; color: #c9d1d9; font-size: 13px; }
37
+ .nav-link:hover { background: #161b22; text-decoration: none; }
38
+ .nav-link.active { background: #161b22; color: #e6edf3; box-shadow: inset 2px 0 0 oklch(0.80 0.16 200); }
39
+
40
+ /* content */
41
+ .content { flex: 1 1 auto; max-width: 840px; min-width: 0; padding: 34px 40px 80px; }
42
+ .content h1 { color: #e6edf3; font-size: 26px; margin: 0 0 16px; }
43
+ .content h2 { color: #e6edf3; font-size: 18px; margin: 30px 0 10px;
44
+ padding-top: 10px; border-top: 1px solid #21262d; }
45
+ .content h3 { color: #e6edf3; font-size: 15px; margin: 22px 0 8px; }
46
+ .content p, .content li { color: #c9d1d9; }
47
+ .content ul, .content ol { padding-left: 22px; margin: 10px 0; }
48
+ .content li { margin: 4px 0; }
49
+ .content blockquote { margin: 14px 0; padding: 2px 14px; border-left: 3px solid oklch(0.80 0.18 60);
50
+ background: #161b22; border-radius: 0 6px 6px 0; color: #8b949e; }
51
+ .content :not(pre) > code { background: #010409; border: 1px solid #21262d;
52
+ border-radius: 4px; padding: 1px 5px; font-size: 12.5px; }
53
+ .content table { border-collapse: collapse; margin: 14px 0; display: block; overflow-x: auto; }
54
+ .content th, .content td { border: 1px solid #21262d; padding: 6px 10px; text-align: left; }
55
+ .content th { color: #e6edf3; background: #161b22; }
56
+
57
+ /* code blocks + copy button */
58
+ .codeblock { position: relative; margin: 14px 0; }
59
+ .codeblock pre { background: #010409; border: 1px solid #21262d; border-radius: 8px;
60
+ padding: 14px 16px; overflow-x: auto; }
61
+ .codeblock pre code { padding: 0; border: 0; background: none; font-size: 12.5px; color: #c9d1d9; }
62
+ .copybtn { position: absolute; top: 8px; right: 8px; padding: 3px 8px; cursor: pointer;
63
+ background: #161b22; border: 1px solid #21262d; border-radius: 5px;
64
+ color: #8b949e; font: 11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
65
+ .copybtn:hover { color: #e6edf3; border-color: oklch(0.80 0.16 200); }
66
+
67
+ @media (max-width: 800px) {
68
+ .layout { flex-direction: column; }
69
+ .sidebar { position: static; height: auto; width: 100%; flex-basis: auto;
70
+ border-right: 0; border-bottom: 1px solid #21262d; }
71
+ .content { padding: 24px 18px 60px; }
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div class="layout">
77
+ <aside class="sidebar">
78
+ <a class="brand" href="../index.html">flashruntime<small>documentation</small></a>
79
+ <div class="search">
80
+ <input id="q" type="search" placeholder="Search docs (press /)" autocomplete="off" spellcheck="false">
81
+ <div id="results"></div>
82
+ </div>
83
+ <nav><div class="nav-section">Getting started</div>
84
+ <a class="nav-link" href="../index.html">FlashRuntime</a>
85
+ <a class="nav-link" href="../get-started.html">Get started</a>
86
+ <div class="nav-section">Tutorials</div>
87
+ <a class="nav-link" href="../tutorials/convnet.html">Tutorial: make a ConvNet fault-tolerant</a>
88
+ <a class="nav-link" href="../tutorials/sklearn-sweeps.html">Tutorial: parallel scikit-learn sweeps</a>
89
+ <a class="nav-link" href="../tutorials/fault-tolerance.html">Tutorial: automatic recovery, explained</a>
90
+ <div class="nav-section">Guides</div>
91
+ <a class="nav-link" href="../guides/pytorch.html">PyTorch guide</a>
92
+ <a class="nav-link" href="../guides/sklearn.html">scikit-learn guide</a>
93
+ <a class="nav-link" href="../guides/huggingface.html">Hugging Face guide</a>
94
+ <a class="nav-link" href="../guides/jobspec-and-isolation.html">JobSpec &amp; isolation guide</a>
95
+ <a class="nav-link" href="../guides/federated-averaging.html">Federated averaging</a>
96
+ <div class="nav-section">Concepts</div>
97
+ <a class="nav-link" href="../concepts/architecture.html">Architecture</a>
98
+ <div class="nav-section">Reference</div>
99
+ <a class="nav-link" href="sdk.html">Reference: SDK (`flashruntime`)</a>
100
+ <a class="nav-link active" href="integrations.html">Reference: integrations (`flashruntime.integrations`)</a>
101
+ <a class="nav-link" href="torch-helper.html">Reference: torch helper (`flashruntime.torch`)</a>
102
+ <a class="nav-link" href="cli.html">Reference: CLI (`flashruntime`)</a>
103
+ <div class="nav-section">Benchmarks</div>
104
+ <a class="nav-link" href="../benchmarks.html">Benchmarks</a></nav>
105
+ </aside>
106
+ <main class="content"><h1 id="reference-integrations-flashruntimeintegrations">Reference: integrations (<code>flashruntime.integrations</code>)</h1>
107
+ <p>Framework adapters that build a <code>CommandWorkload</code> for you. Each is a small,
108
+ framework-neutral function — the recipe axis of the four-axis architecture. No
109
+ adapter imports its framework at the top level (the import is paid only in your
110
+ training process).</p>
111
+ <p>Signatures below are exact; each entry says, in one line, <em>why it exists</em>.</p>
112
+ <hr />
113
+ <h2 id="integrationspytorch-fr_torch"><code>integrations.pytorch</code> (<code>fr_torch</code>)</h2>
114
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code class="language-python">def ddp(
115
+ script,
116
+ *,
117
+ source=&quot;.&quot;,
118
+ nproc_per_node=2,
119
+ nnodes=1,
120
+ script_args=&quot;&quot;,
121
+ env=None,
122
+ ) -&gt; CommandWorkload: ...
123
+ </code></pre></div>
124
+ <p>Builds the <code>torchrun</code> launch command for a PyTorch script — the launch
125
+ conventions only; DDP is wired by your code (or <code>flashruntime.torch.prepare</code>).
126
+ Emits <code>torchrun --nproc-per-node=N --nnodes=1 --standalone
127
+ --local-addr=127.0.0.1 &lt;script&gt; &lt;args&gt;</code>. <code>nnodes &gt; 1</code> raises
128
+ <code>NotImplementedError</code> (multi-node rendezvous is a later slice). See the
129
+ <a href="../guides/pytorch.html">PyTorch guide</a>.</p>
130
+ <hr />
131
+ <h2 id="integrationssklearn-fr_sklearn"><code>integrations.sklearn</code> (<code>fr_sklearn</code>)</h2>
132
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code class="language-python">def sweep(
133
+ script,
134
+ task_params,
135
+ *,
136
+ source=&quot;.&quot;,
137
+ metric=&quot;accuracy_mean&quot;,
138
+ maximize=True,
139
+ python=&quot;python&quot;,
140
+ ) -&gt; CommandWorkload: ...
141
+
142
+ def hpo(script, grid, **kwargs) -&gt; CommandWorkload: ...
143
+ </code></pre></div>
144
+ <ul>
145
+ <li><strong><code>sweep</code></strong> — one independent task per params dict; sets
146
+ <code>outputs.primary_metric=metric</code> so <code>run.best_trial()</code> needs no arguments.</li>
147
+ <li><strong><code>hpo</code></strong> — Cartesian-grid sugar over <code>sweep</code>
148
+ (<code>{"model": ["logreg", "rf"], "C": [0.1, 1]}</code> → 4 trials).</li>
149
+ </ul>
150
+ <p>Distributes across runs, never inside a single <code>.fit()</code>. See the
151
+ <a href="../guides/sklearn.html">scikit-learn guide</a>.</p>
152
+ <hr />
153
+ <h2 id="integrationshuggingface-fr_hf"><code>integrations.huggingface</code> (<code>fr_hf</code>)</h2>
154
+ <div class="codeblock"><button class="copybtn" type="button" aria-label="Copy code to clipboard">Copy</button><pre><code class="language-python">def trainer(script, *, source=&quot;.&quot;, nproc_per_node=1, script_args=&quot;&quot;) -&gt; CommandWorkload: ...
155
+
156
+ def latest_checkpoint(output_dir) -&gt; str | None: ...
157
+
158
+ def flashruntime_callback(): ... # returns a transformers TrainerCallback
159
+ </code></pre></div>
160
+ <ul>
161
+ <li><strong><code>trainer</code></strong> — a thin wrapper over <code>pytorch.ddp()</code>; launching an HF Trainer
162
+ job is just the PyTorch path (Trainer wraps DDP/FSDP internally under
163
+ <code>torchrun</code>).</li>
164
+ <li><strong><code>latest_checkpoint</code></strong> — newest <code>checkpoint-*</code> dir with a <strong>valid</strong> manifest
165
+ (or <code>None</code>); pass straight to <code>trainer.train(resume_from_checkpoint=...)</code>.</li>
166
+ <li><strong><code>flashruntime_callback</code></strong> — a <code>TrainerCallback</code> whose <code>on_save</code> commits a
167
+ verified manifest and whose <code>on_log</code> relays metrics through
168
+ <code>flashruntime.torch.log_metrics</code>. The <code>transformers</code> import is paid inside
169
+ this factory, in your process.</li>
170
+ </ul>
171
+ <p>See the <a href="../guides/huggingface.html">Hugging Face guide</a>.</p>
172
+ <hr />
173
+ <h2 id="the-extensibility-pattern">The extensibility pattern</h2>
174
+ <p>Every adapter returns a <code>CommandWorkload</code> and reuses the same
175
+ launch/collect/recover machinery. To teach FlashRuntime a new framework, write a
176
+ function under <code>flashruntime/integrations/</code> that returns a <code>CommandWorkload</code>
177
+ describing <em>what to run</em> (and, if the framework has hooks, maps them onto
178
+ <code>write_manifest</code> / <code>log_metrics</code>). No core change is required — that is the
179
+ four-axis payoff. The <a href="sdk.html">SDK reference</a> documents the <code>CommandWorkload</code>
180
+ shape you build.</p></main>
181
+ </div>
182
+
183
+ <script>
184
+ // ---- client-side search: fetch the builder's index, filter as you type -----
185
+ // (<=60 lines, vanilla JS, no external anything — the index is a sibling file.)
186
+ let INDEX = [];
187
+ // Path from THIS page back to the site root. The index stores root-relative
188
+ // URLs (e.g. "tutorials/deep.html"); we fetch it and build result links
189
+ // against ROOT so a page in a subdir resolves them too (see build_docs.py).
190
+ const ROOT = "../";
191
+ const q = document.getElementById("q");
192
+ const results = document.getElementById("results");
193
+ const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
194
+ ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
195
+ fetch(ROOT + "search-index.json").then((r) => r.json()).then((d) => { INDEX = d; }).catch(() => {});
196
+
197
+ // A short context window around the first match, so a hit shows WHY it matched.
198
+ function snippet(text, needle) {
199
+ const i = text.toLowerCase().indexOf(needle);
200
+ if (i < 0) return "";
201
+ const start = Math.max(0, i - 32);
202
+ return (start > 0 ? "…" : "") + text.slice(start, i + needle.length + 44).trim() + "…";
203
+ }
204
+ function runSearch() {
205
+ const needle = q.value.trim().toLowerCase();
206
+ if (!needle) { results.className = ""; results.innerHTML = ""; return; }
207
+ const hits = INDEX.map((p) => {
208
+ const inTitle = p.title.toLowerCase().includes(needle);
209
+ const inText = p.text.toLowerCase().includes(needle);
210
+ if (!inTitle && !inText) return null;
211
+ return { url: p.url, title: p.title, snip: inText ? snippet(p.text, needle) : "" };
212
+ }).filter(Boolean).slice(0, 20);
213
+ results.className = "open";
214
+ results.innerHTML = hits.length
215
+ ? hits.map((h) => '<a href="' + ROOT + h.url + '"><b>' + esc(h.title) + "</b>" +
216
+ (h.snip ? "<span>" + esc(h.snip) + "</span>" : "") + "</a>").join("")
217
+ : '<div class="nohit">no matches</div>';
218
+ }
219
+ q.addEventListener("input", runSearch);
220
+ q.addEventListener("focus", runSearch);
221
+ document.addEventListener("click", (e) => {
222
+ if (!e.target.closest(".search")) { results.className = ""; } // dismiss on outside click
223
+ });
224
+ document.addEventListener("keydown", (e) => { // "/" focuses search, like PyTorch docs
225
+ if (e.key === "/" && document.activeElement !== q) { e.preventDefault(); q.focus(); }
226
+ });
227
+
228
+ // ---- copy buttons: read the <pre>'s own text into the clipboard ------------
229
+ document.querySelectorAll(".copybtn").forEach((btn) => {
230
+ btn.addEventListener("click", () => {
231
+ const code = btn.parentElement.querySelector("pre").innerText;
232
+ navigator.clipboard.writeText(code).then(() => {
233
+ const was = btn.textContent; btn.textContent = "Copied";
234
+ setTimeout(() => { btn.textContent = was; }, 1200);
235
+ }).catch(() => {});
236
+ });
237
+ });
238
+ </script>
239
+ </body>
240
+ </html>