hearth-engine 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.
- hearth_engine-0.1.0/Cargo.lock +423 -0
- hearth_engine-0.1.0/Cargo.toml +14 -0
- hearth_engine-0.1.0/PKG-INFO +109 -0
- hearth_engine-0.1.0/README.md +92 -0
- hearth_engine-0.1.0/crates/hearth-core/Cargo.toml +12 -0
- hearth_engine-0.1.0/crates/hearth-core/examples/a6000.rs +33 -0
- hearth_engine-0.1.0/crates/hearth-core/src/budget.rs +170 -0
- hearth_engine-0.1.0/crates/hearth-core/src/fleet.rs +267 -0
- hearth_engine-0.1.0/crates/hearth-core/src/lib.rs +29 -0
- hearth_engine-0.1.0/crates/hearth-core/src/residency.rs +247 -0
- hearth_engine-0.1.0/crates/hearth-core/tests/fleet.rs +300 -0
- hearth_engine-0.1.0/crates/hearth-core/tests/residency.rs +382 -0
- hearth_engine-0.1.0/crates/hearth-py/Cargo.toml +17 -0
- hearth_engine-0.1.0/crates/hearth-py/src/lib.rs +162 -0
- hearth_engine-0.1.0/pyproject.toml +29 -0
- hearth_engine-0.1.0/python/hearth/__init__.py +96 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "aho-corasick"
|
|
7
|
+
version = "1.1.5"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"memchr",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "autocfg"
|
|
16
|
+
version = "1.5.1"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
|
19
|
+
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "bitflags"
|
|
22
|
+
version = "2.13.1"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "cfg-if"
|
|
28
|
+
version = "1.0.4"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "convert_case"
|
|
34
|
+
version = "0.6.0"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
|
37
|
+
dependencies = [
|
|
38
|
+
"unicode-segmentation",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "ctor"
|
|
43
|
+
version = "0.2.9"
|
|
44
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
45
|
+
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
|
46
|
+
dependencies = [
|
|
47
|
+
"quote",
|
|
48
|
+
"syn 2.0.119",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "hearth-core"
|
|
53
|
+
version = "0.1.0"
|
|
54
|
+
dependencies = [
|
|
55
|
+
"serde",
|
|
56
|
+
"serde_json",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "hearth-node"
|
|
61
|
+
version = "0.1.0"
|
|
62
|
+
dependencies = [
|
|
63
|
+
"hearth-core",
|
|
64
|
+
"napi",
|
|
65
|
+
"napi-build",
|
|
66
|
+
"napi-derive",
|
|
67
|
+
"serde_json",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[[package]]
|
|
71
|
+
name = "hearth-py"
|
|
72
|
+
version = "0.1.0"
|
|
73
|
+
dependencies = [
|
|
74
|
+
"hearth-core",
|
|
75
|
+
"pyo3",
|
|
76
|
+
"serde_json",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[[package]]
|
|
80
|
+
name = "heck"
|
|
81
|
+
version = "0.5.0"
|
|
82
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
83
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "indoc"
|
|
87
|
+
version = "2.0.7"
|
|
88
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
89
|
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
90
|
+
dependencies = [
|
|
91
|
+
"rustversion",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
[[package]]
|
|
95
|
+
name = "itoa"
|
|
96
|
+
version = "1.0.18"
|
|
97
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
98
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "libc"
|
|
102
|
+
version = "0.2.189"
|
|
103
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
104
|
+
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|
105
|
+
|
|
106
|
+
[[package]]
|
|
107
|
+
name = "libloading"
|
|
108
|
+
version = "0.8.9"
|
|
109
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
110
|
+
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
111
|
+
dependencies = [
|
|
112
|
+
"cfg-if",
|
|
113
|
+
"windows-link",
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
[[package]]
|
|
117
|
+
name = "memchr"
|
|
118
|
+
version = "2.8.3"
|
|
119
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
120
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "memoffset"
|
|
124
|
+
version = "0.9.1"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
127
|
+
dependencies = [
|
|
128
|
+
"autocfg",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
[[package]]
|
|
132
|
+
name = "napi"
|
|
133
|
+
version = "2.16.17"
|
|
134
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
135
|
+
checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
|
|
136
|
+
dependencies = [
|
|
137
|
+
"bitflags",
|
|
138
|
+
"ctor",
|
|
139
|
+
"napi-derive",
|
|
140
|
+
"napi-sys",
|
|
141
|
+
"once_cell",
|
|
142
|
+
"serde",
|
|
143
|
+
"serde_json",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[[package]]
|
|
147
|
+
name = "napi-build"
|
|
148
|
+
version = "2.4.1"
|
|
149
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
150
|
+
checksum = "60fdf9b392c50e7c4170fa633bd909490ed7835cea4c046776d1a4dd8d2ae0ab"
|
|
151
|
+
|
|
152
|
+
[[package]]
|
|
153
|
+
name = "napi-derive"
|
|
154
|
+
version = "2.16.13"
|
|
155
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
156
|
+
checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
|
|
157
|
+
dependencies = [
|
|
158
|
+
"cfg-if",
|
|
159
|
+
"convert_case",
|
|
160
|
+
"napi-derive-backend",
|
|
161
|
+
"proc-macro2",
|
|
162
|
+
"quote",
|
|
163
|
+
"syn 2.0.119",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
[[package]]
|
|
167
|
+
name = "napi-derive-backend"
|
|
168
|
+
version = "1.0.75"
|
|
169
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
170
|
+
checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
|
|
171
|
+
dependencies = [
|
|
172
|
+
"convert_case",
|
|
173
|
+
"once_cell",
|
|
174
|
+
"proc-macro2",
|
|
175
|
+
"quote",
|
|
176
|
+
"regex",
|
|
177
|
+
"semver",
|
|
178
|
+
"syn 2.0.119",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "napi-sys"
|
|
183
|
+
version = "2.4.0"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
|
|
186
|
+
dependencies = [
|
|
187
|
+
"libloading",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
[[package]]
|
|
191
|
+
name = "once_cell"
|
|
192
|
+
version = "1.21.4"
|
|
193
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
195
|
+
|
|
196
|
+
[[package]]
|
|
197
|
+
name = "portable-atomic"
|
|
198
|
+
version = "1.15.0"
|
|
199
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
200
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
201
|
+
|
|
202
|
+
[[package]]
|
|
203
|
+
name = "proc-macro2"
|
|
204
|
+
version = "1.0.107"
|
|
205
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
206
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
207
|
+
dependencies = [
|
|
208
|
+
"unicode-ident",
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
[[package]]
|
|
212
|
+
name = "pyo3"
|
|
213
|
+
version = "0.22.6"
|
|
214
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
215
|
+
checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
|
|
216
|
+
dependencies = [
|
|
217
|
+
"cfg-if",
|
|
218
|
+
"indoc",
|
|
219
|
+
"libc",
|
|
220
|
+
"memoffset",
|
|
221
|
+
"once_cell",
|
|
222
|
+
"portable-atomic",
|
|
223
|
+
"pyo3-build-config",
|
|
224
|
+
"pyo3-ffi",
|
|
225
|
+
"pyo3-macros",
|
|
226
|
+
"unindent",
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
[[package]]
|
|
230
|
+
name = "pyo3-build-config"
|
|
231
|
+
version = "0.22.6"
|
|
232
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
233
|
+
checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
|
|
234
|
+
dependencies = [
|
|
235
|
+
"once_cell",
|
|
236
|
+
"target-lexicon",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
[[package]]
|
|
240
|
+
name = "pyo3-ffi"
|
|
241
|
+
version = "0.22.6"
|
|
242
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
243
|
+
checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
|
|
244
|
+
dependencies = [
|
|
245
|
+
"libc",
|
|
246
|
+
"pyo3-build-config",
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
[[package]]
|
|
250
|
+
name = "pyo3-macros"
|
|
251
|
+
version = "0.22.6"
|
|
252
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
+
checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
|
|
254
|
+
dependencies = [
|
|
255
|
+
"proc-macro2",
|
|
256
|
+
"pyo3-macros-backend",
|
|
257
|
+
"quote",
|
|
258
|
+
"syn 2.0.119",
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
[[package]]
|
|
262
|
+
name = "pyo3-macros-backend"
|
|
263
|
+
version = "0.22.6"
|
|
264
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
265
|
+
checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
|
|
266
|
+
dependencies = [
|
|
267
|
+
"heck",
|
|
268
|
+
"proc-macro2",
|
|
269
|
+
"pyo3-build-config",
|
|
270
|
+
"quote",
|
|
271
|
+
"syn 2.0.119",
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
[[package]]
|
|
275
|
+
name = "quote"
|
|
276
|
+
version = "1.0.47"
|
|
277
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
278
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
279
|
+
dependencies = [
|
|
280
|
+
"proc-macro2",
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
[[package]]
|
|
284
|
+
name = "regex"
|
|
285
|
+
version = "1.13.1"
|
|
286
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
287
|
+
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
|
288
|
+
dependencies = [
|
|
289
|
+
"aho-corasick",
|
|
290
|
+
"memchr",
|
|
291
|
+
"regex-automata",
|
|
292
|
+
"regex-syntax",
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "regex-automata"
|
|
297
|
+
version = "0.4.18"
|
|
298
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
+
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
|
300
|
+
dependencies = [
|
|
301
|
+
"aho-corasick",
|
|
302
|
+
"memchr",
|
|
303
|
+
"regex-syntax",
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
[[package]]
|
|
307
|
+
name = "regex-syntax"
|
|
308
|
+
version = "0.8.11"
|
|
309
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
310
|
+
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
311
|
+
|
|
312
|
+
[[package]]
|
|
313
|
+
name = "rustversion"
|
|
314
|
+
version = "1.0.23"
|
|
315
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
316
|
+
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
|
317
|
+
|
|
318
|
+
[[package]]
|
|
319
|
+
name = "semver"
|
|
320
|
+
version = "1.0.28"
|
|
321
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
322
|
+
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
|
323
|
+
|
|
324
|
+
[[package]]
|
|
325
|
+
name = "serde"
|
|
326
|
+
version = "1.0.229"
|
|
327
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
328
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
329
|
+
dependencies = [
|
|
330
|
+
"serde_core",
|
|
331
|
+
"serde_derive",
|
|
332
|
+
]
|
|
333
|
+
|
|
334
|
+
[[package]]
|
|
335
|
+
name = "serde_core"
|
|
336
|
+
version = "1.0.229"
|
|
337
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
338
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
339
|
+
dependencies = [
|
|
340
|
+
"serde_derive",
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
[[package]]
|
|
344
|
+
name = "serde_derive"
|
|
345
|
+
version = "1.0.229"
|
|
346
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
347
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
348
|
+
dependencies = [
|
|
349
|
+
"proc-macro2",
|
|
350
|
+
"quote",
|
|
351
|
+
"syn 3.0.4",
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
[[package]]
|
|
355
|
+
name = "serde_json"
|
|
356
|
+
version = "1.0.151"
|
|
357
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
358
|
+
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
|
359
|
+
dependencies = [
|
|
360
|
+
"itoa",
|
|
361
|
+
"memchr",
|
|
362
|
+
"serde",
|
|
363
|
+
"serde_core",
|
|
364
|
+
"zmij",
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
[[package]]
|
|
368
|
+
name = "syn"
|
|
369
|
+
version = "2.0.119"
|
|
370
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
371
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
372
|
+
dependencies = [
|
|
373
|
+
"proc-macro2",
|
|
374
|
+
"quote",
|
|
375
|
+
"unicode-ident",
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
[[package]]
|
|
379
|
+
name = "syn"
|
|
380
|
+
version = "3.0.4"
|
|
381
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
382
|
+
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
|
|
383
|
+
dependencies = [
|
|
384
|
+
"proc-macro2",
|
|
385
|
+
"quote",
|
|
386
|
+
"unicode-ident",
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
[[package]]
|
|
390
|
+
name = "target-lexicon"
|
|
391
|
+
version = "0.12.16"
|
|
392
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
393
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
394
|
+
|
|
395
|
+
[[package]]
|
|
396
|
+
name = "unicode-ident"
|
|
397
|
+
version = "1.0.24"
|
|
398
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
399
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
400
|
+
|
|
401
|
+
[[package]]
|
|
402
|
+
name = "unicode-segmentation"
|
|
403
|
+
version = "1.13.3"
|
|
404
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
405
|
+
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
406
|
+
|
|
407
|
+
[[package]]
|
|
408
|
+
name = "unindent"
|
|
409
|
+
version = "0.2.4"
|
|
410
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
411
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
412
|
+
|
|
413
|
+
[[package]]
|
|
414
|
+
name = "windows-link"
|
|
415
|
+
version = "0.2.1"
|
|
416
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
417
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
418
|
+
|
|
419
|
+
[[package]]
|
|
420
|
+
name = "zmij"
|
|
421
|
+
version = "1.0.23"
|
|
422
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
423
|
+
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
resolver = "2"
|
|
3
|
+
members = ["crates/hearth-core", "crates/hearth-py"]
|
|
4
|
+
|
|
5
|
+
[workspace.package]
|
|
6
|
+
version = "0.1.0"
|
|
7
|
+
edition = "2021"
|
|
8
|
+
license = "BUSL-1.1"
|
|
9
|
+
repository = "https://github.com/aiassistsecure/hearth"
|
|
10
|
+
authors = ["Interchained LLC"]
|
|
11
|
+
|
|
12
|
+
[workspace.dependencies]
|
|
13
|
+
serde = { version = "1", features = ["derive"] }
|
|
14
|
+
serde_json = "1"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hearth-engine
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
7
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
8
|
+
Summary: Deterministic model residency — keep declared models warm, and tell the truth about which ones are.
|
|
9
|
+
Keywords: llm,inference,gpu,vram,residency,ollama,llama.cpp
|
|
10
|
+
Author: Interchained LLC
|
|
11
|
+
License: BUSL-1.1
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
14
|
+
Project-URL: Homepage, https://github.com/Eth-Interchained/hearth
|
|
15
|
+
Project-URL: Repository, https://github.com/Eth-Interchained/hearth
|
|
16
|
+
|
|
17
|
+
# hearth
|
|
18
|
+
|
|
19
|
+
**Deterministic model residency.** Keep declared models warm, and tell the truth about which ones are.
|
|
20
|
+
|
|
21
|
+
Not an inference engine. llama.cpp and vLLM have spent years on kernels, samplers and tokenizers, and none of that is the problem. The problem is that no serving stack will *promise* a model stays loaded, and none of them can say *why* one stopped being.
|
|
22
|
+
|
|
23
|
+
## The night this came from
|
|
24
|
+
|
|
25
|
+
A PIN operator on one rented RTX A6000 stopped answering. Requests hung, then failed. Four pull requests landed against the streaming path in a single evening and none of them were the cause — because the cause was never visible. Three completely different failures were arriving as the same timeout:
|
|
26
|
+
|
|
27
|
+
- the runtime evicted a model to free VRAM,
|
|
28
|
+
- the host detached the GPU and gave it to another tenant,
|
|
29
|
+
- a 32B model was simply still loading.
|
|
30
|
+
|
|
31
|
+
One of those is a capacity problem you own. One is your provider's and no configuration will touch it. One is not a problem at all. A timeout cannot tell you which, so all three got "fixed" repeatedly and none of them went away.
|
|
32
|
+
|
|
33
|
+
Worse, the operator was being **scored down** for a card their host reclaimed. A reputation system fed that kind of data slowly deletes its own honest operators.
|
|
34
|
+
|
|
35
|
+
## What it does
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
$ hearth status
|
|
39
|
+
42.0 / 44.2 GiB held (5 declared, 2 admitted)
|
|
40
|
+
muse-local:latest resident for 14203s
|
|
41
|
+
deepseek-r1:32b loading for 47s
|
|
42
|
+
gemma4:26b not admitted — short by 14.8 GiB
|
|
43
|
+
qwen3.6:27b not admitted — short by 15.8 GiB
|
|
44
|
+
gemma4-extract:31b not admitted — short by 17.8 GiB
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Three things, none of which you can get today:
|
|
48
|
+
|
|
49
|
+
**1. Residency is a named state with a named reason.**
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Unknown never probed
|
|
53
|
+
Loading weights materializing — with elapsed, so it can say how long
|
|
54
|
+
Resident loaded AND answering AND accounted for
|
|
55
|
+
Lost with a reason: Evicted · GpuDetached · ProcessExited · Unhealthy
|
|
56
|
+
Failed won't load, and what the runtime said
|
|
57
|
+
Stopped unloaded on purpose
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`Evicted` and `GpuDetached` are the two states nothing else reports, and they were the two that mattered. They are distinguished by one bit — whether the GPU was still present when the probe failed — and that bit is the entire diagnosis.
|
|
61
|
+
|
|
62
|
+
**2. The card's size is arithmetic, checked before anything loads.**
|
|
63
|
+
|
|
64
|
+
Declare four 20 GiB models on a 48 GiB card and no runtime errors. It loads, evicts, loads, evicts, forever, and presents as "the models got slow." hearth refuses the fifth model at declare time and tells you it was short by 17.8 GiB. Nothing is ever evicted to make room for a load — if it doesn't fit, the honest answer is that it doesn't fit.
|
|
65
|
+
|
|
66
|
+
**3. Routers get an answer they can act on.**
|
|
67
|
+
|
|
68
|
+
| answer | what a router should do |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `Ready` | send it |
|
|
71
|
+
| `Warming{for_ms}` | wait, or try elsewhere — but do **not** fault this node |
|
|
72
|
+
| `Lost{GpuDetached}` | try elsewhere, and do **not** score this operator down |
|
|
73
|
+
| `Lost{Evicted}` | try elsewhere; this box is over-committed |
|
|
74
|
+
| `NotAdmitted{short}` | stop asking — it will never fit here |
|
|
75
|
+
| `Unknown` | we genuinely don't know yet, and we say so |
|
|
76
|
+
|
|
77
|
+
## Design rules
|
|
78
|
+
|
|
79
|
+
**Nothing fails on a clock.** A 32B materializing over a network fabric can legitimately spend minutes before its first token, and killing it at an arbitrary deadline turns a slow success into a fast failure. `Loading` reports how long it has been loading; deciding what to do about that belongs to whoever knows if a human is waiting. Progress is reported — patience is a policy, not a constant.
|
|
80
|
+
|
|
81
|
+
**`Loading` is not `Ready`.** The most common way a serving stack lies is routing to something still coming up and calling the inevitable timeout an error.
|
|
82
|
+
|
|
83
|
+
**Declaration order is priority order.** First fit, never best fit. Reordering to squeeze in one more model would silently demote whatever the operator listed first, and on a serving box first means most important. A planner that outsmarts the operator surprises them at 3am.
|
|
84
|
+
|
|
85
|
+
**The reserve is never planned into.** Weights aren't the whole cost — KV cache grows with context and parallelism, the CUDA context is hundreds of megabytes, and fragmentation is real on a card that's been up for weeks.
|
|
86
|
+
|
|
87
|
+
## Status
|
|
88
|
+
|
|
89
|
+
`hearth-core` — state machine, VRAM planner, fleet routing. 33 tests, all pure logic, no GPU required to run them.
|
|
90
|
+
|
|
91
|
+
Next: supervisor over `llama-server` children · NVML probe · HTTP surface (OpenAI-compatible + `/residency`) · CLI · napi + PyO3 bindings · NEDB event log.
|
|
92
|
+
|
|
93
|
+
That last one is the interesting one. Every state transition becomes an event in [NEDB](https://github.com/Eth-Interchained/nedb), which is bi-temporal — so *"what was resident **as of** 03:14?"* is a real query against a real causal chain. When a model goes cold at 3am you get the answer instead of a theory.
|
|
94
|
+
|
|
95
|
+
## Integration
|
|
96
|
+
|
|
97
|
+
hearth speaks OpenAI-compatible, so [pin-clientd](https://github.com/aiassistsecure/pin-clientd) works with it today: set `apiMode: "openai"` and point `inferenceUri` at hearth. `/residency` then adds the truth that the OpenAI shape has no way to express.
|
|
98
|
+
|
|
99
|
+
## Build
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cargo test # 33 tests, no GPU needed
|
|
103
|
+
cargo run --example a6000
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
© Interchained LLC · BUSL-1.1 (converts to Apache-2.0 on 2030-08-27)
|
|
109
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# hearth
|
|
2
|
+
|
|
3
|
+
**Deterministic model residency.** Keep declared models warm, and tell the truth about which ones are.
|
|
4
|
+
|
|
5
|
+
Not an inference engine. llama.cpp and vLLM have spent years on kernels, samplers and tokenizers, and none of that is the problem. The problem is that no serving stack will *promise* a model stays loaded, and none of them can say *why* one stopped being.
|
|
6
|
+
|
|
7
|
+
## The night this came from
|
|
8
|
+
|
|
9
|
+
A PIN operator on one rented RTX A6000 stopped answering. Requests hung, then failed. Four pull requests landed against the streaming path in a single evening and none of them were the cause — because the cause was never visible. Three completely different failures were arriving as the same timeout:
|
|
10
|
+
|
|
11
|
+
- the runtime evicted a model to free VRAM,
|
|
12
|
+
- the host detached the GPU and gave it to another tenant,
|
|
13
|
+
- a 32B model was simply still loading.
|
|
14
|
+
|
|
15
|
+
One of those is a capacity problem you own. One is your provider's and no configuration will touch it. One is not a problem at all. A timeout cannot tell you which, so all three got "fixed" repeatedly and none of them went away.
|
|
16
|
+
|
|
17
|
+
Worse, the operator was being **scored down** for a card their host reclaimed. A reputation system fed that kind of data slowly deletes its own honest operators.
|
|
18
|
+
|
|
19
|
+
## What it does
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
$ hearth status
|
|
23
|
+
42.0 / 44.2 GiB held (5 declared, 2 admitted)
|
|
24
|
+
muse-local:latest resident for 14203s
|
|
25
|
+
deepseek-r1:32b loading for 47s
|
|
26
|
+
gemma4:26b not admitted — short by 14.8 GiB
|
|
27
|
+
qwen3.6:27b not admitted — short by 15.8 GiB
|
|
28
|
+
gemma4-extract:31b not admitted — short by 17.8 GiB
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Three things, none of which you can get today:
|
|
32
|
+
|
|
33
|
+
**1. Residency is a named state with a named reason.**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Unknown never probed
|
|
37
|
+
Loading weights materializing — with elapsed, so it can say how long
|
|
38
|
+
Resident loaded AND answering AND accounted for
|
|
39
|
+
Lost with a reason: Evicted · GpuDetached · ProcessExited · Unhealthy
|
|
40
|
+
Failed won't load, and what the runtime said
|
|
41
|
+
Stopped unloaded on purpose
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`Evicted` and `GpuDetached` are the two states nothing else reports, and they were the two that mattered. They are distinguished by one bit — whether the GPU was still present when the probe failed — and that bit is the entire diagnosis.
|
|
45
|
+
|
|
46
|
+
**2. The card's size is arithmetic, checked before anything loads.**
|
|
47
|
+
|
|
48
|
+
Declare four 20 GiB models on a 48 GiB card and no runtime errors. It loads, evicts, loads, evicts, forever, and presents as "the models got slow." hearth refuses the fifth model at declare time and tells you it was short by 17.8 GiB. Nothing is ever evicted to make room for a load — if it doesn't fit, the honest answer is that it doesn't fit.
|
|
49
|
+
|
|
50
|
+
**3. Routers get an answer they can act on.**
|
|
51
|
+
|
|
52
|
+
| answer | what a router should do |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `Ready` | send it |
|
|
55
|
+
| `Warming{for_ms}` | wait, or try elsewhere — but do **not** fault this node |
|
|
56
|
+
| `Lost{GpuDetached}` | try elsewhere, and do **not** score this operator down |
|
|
57
|
+
| `Lost{Evicted}` | try elsewhere; this box is over-committed |
|
|
58
|
+
| `NotAdmitted{short}` | stop asking — it will never fit here |
|
|
59
|
+
| `Unknown` | we genuinely don't know yet, and we say so |
|
|
60
|
+
|
|
61
|
+
## Design rules
|
|
62
|
+
|
|
63
|
+
**Nothing fails on a clock.** A 32B materializing over a network fabric can legitimately spend minutes before its first token, and killing it at an arbitrary deadline turns a slow success into a fast failure. `Loading` reports how long it has been loading; deciding what to do about that belongs to whoever knows if a human is waiting. Progress is reported — patience is a policy, not a constant.
|
|
64
|
+
|
|
65
|
+
**`Loading` is not `Ready`.** The most common way a serving stack lies is routing to something still coming up and calling the inevitable timeout an error.
|
|
66
|
+
|
|
67
|
+
**Declaration order is priority order.** First fit, never best fit. Reordering to squeeze in one more model would silently demote whatever the operator listed first, and on a serving box first means most important. A planner that outsmarts the operator surprises them at 3am.
|
|
68
|
+
|
|
69
|
+
**The reserve is never planned into.** Weights aren't the whole cost — KV cache grows with context and parallelism, the CUDA context is hundreds of megabytes, and fragmentation is real on a card that's been up for weeks.
|
|
70
|
+
|
|
71
|
+
## Status
|
|
72
|
+
|
|
73
|
+
`hearth-core` — state machine, VRAM planner, fleet routing. 33 tests, all pure logic, no GPU required to run them.
|
|
74
|
+
|
|
75
|
+
Next: supervisor over `llama-server` children · NVML probe · HTTP surface (OpenAI-compatible + `/residency`) · CLI · napi + PyO3 bindings · NEDB event log.
|
|
76
|
+
|
|
77
|
+
That last one is the interesting one. Every state transition becomes an event in [NEDB](https://github.com/Eth-Interchained/nedb), which is bi-temporal — so *"what was resident **as of** 03:14?"* is a real query against a real causal chain. When a model goes cold at 3am you get the answer instead of a theory.
|
|
78
|
+
|
|
79
|
+
## Integration
|
|
80
|
+
|
|
81
|
+
hearth speaks OpenAI-compatible, so [pin-clientd](https://github.com/aiassistsecure/pin-clientd) works with it today: set `apiMode: "openai"` and point `inferenceUri` at hearth. `/residency` then adds the truth that the OpenAI shape has no way to express.
|
|
82
|
+
|
|
83
|
+
## Build
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cargo test # 33 tests, no GPU needed
|
|
87
|
+
cargo run --example a6000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
© Interchained LLC · BUSL-1.1 (converts to Apache-2.0 on 2030-08-27)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "hearth-core"
|
|
3
|
+
description = "Deterministic model residency: pin models warm, and tell the truth about which ones are."
|
|
4
|
+
version.workspace = true
|
|
5
|
+
edition.workspace = true
|
|
6
|
+
license.workspace = true
|
|
7
|
+
repository.workspace = true
|
|
8
|
+
authors.workspace = true
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
serde = { workspace = true }
|
|
12
|
+
serde_json = { workspace = true }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//! What actually fits on one RTX A6000, given the roster that was registered
|
|
2
|
+
//! when PIN stopped answering.
|
|
3
|
+
use hearth_core::budget::gib;
|
|
4
|
+
use hearth_core::{plan, Budget, Declared, GIB};
|
|
5
|
+
|
|
6
|
+
fn m(model: &str, w: u64, kv: u64) -> Declared {
|
|
7
|
+
Declared {
|
|
8
|
+
model: model.into(),
|
|
9
|
+
weights_bytes: w,
|
|
10
|
+
kv_bytes: kv,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fn main() {
|
|
15
|
+
let card = Budget::with_reserve_pct(48 * GIB, 8);
|
|
16
|
+
println!(
|
|
17
|
+
"RTX A6000 — {:.0} GiB total, {:.1} GiB reserved, {:.1} GiB usable\n",
|
|
18
|
+
gib(card.total_bytes),
|
|
19
|
+
gib(card.reserve_bytes),
|
|
20
|
+
gib(card.usable_bytes())
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
let roster = vec![
|
|
24
|
+
m("muse-local:latest", 20 * GIB, GIB),
|
|
25
|
+
m("deepseek-r1:32b", 20 * GIB, GIB),
|
|
26
|
+
m("gemma4:26b", 16 * GIB, GIB),
|
|
27
|
+
m("qwen3.6:27b", 17 * GIB, GIB),
|
|
28
|
+
m("gemma4-extract:31b", 19 * GIB, GIB),
|
|
29
|
+
];
|
|
30
|
+
let p = plan(card, &roster);
|
|
31
|
+
println!("{}", p.explain());
|
|
32
|
+
println!("\nheadroom: {:.1} GiB", gib(p.headroom_bytes()));
|
|
33
|
+
}
|