huddle-cluster 1.3.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rahad Bhuiya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,466 @@
1
+ Metadata-Version: 2.3
2
+ Name: huddle-cluster
3
+ Version: 1.3.1
4
+ Summary: A penguin-inspired self-organizing server load balancer with adaptive thermal eviction
5
+ Home-page: https://github.com/rahadbhuiya/HuddleCluster
6
+ Author: Rahad Bhuiya
7
+ Author-email: Rahad Bhuiya <rahadbhuiya@gmail.com>
8
+ License: MIT
9
+ Keywords: load-balancer,load-balancing,distributed-systems,bio-inspired,penguin,adaptive,anomaly-detection
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: System :: Distributed Computing
18
+ Classifier: Topic :: System :: Networking
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Project-URL: Homepage, https://github.com/rahadbhuiya/HuddleCluster
22
+ Project-URL: Repository, https://github.com/rahadbhuiya/HuddleCluster
23
+ Project-URL: Bug Tracker, https://github.com/rahadbhuiya/HuddleCluster/issues
24
+
25
+ # HuddleCluster
26
+
27
+ A penguin-inspired, self-organizing server load balancer with adaptive thermal eviction.
28
+
29
+ **Author:** Rahad Bhuiya
30
+ **Version:** 1.3.0
31
+ **License:** MIT
32
+ **Paper:** [HuddleCluster: A Penguin-Inspired Self-Organizing Load Balancer with Adaptive Thermal Eviction](https://github.com/rahadbhuiya/HuddleCluster/blob/main/docs/HuddleCluster_Paper.pdf)
33
+
34
+ ---
35
+
36
+ ## The Idea
37
+
38
+ Emperor Penguins survive Antarctic winters by forming huddles. Penguins on the cold outer edge
39
+ push inward toward warmth, while those in the center gradually rotate outward to rest — with no
40
+ central coordinator, only local temperature thresholds.
41
+
42
+ HuddleCluster maps this directly to server scheduling:
43
+
44
+ - **Inner ring** — active servers handling requests (warm)
45
+ - **Outer ring** — resting servers recovering from load (cool)
46
+ - **Temperature** — a composite EMA score derived from relative latency anomaly, CPU, memory, connections, and error rate
47
+ - **Rotation** — overheated servers evict to outer ring; cooled servers return to inner ring automatically
48
+
49
+ The key innovation is **relative latency anomaly scoring**: instead of comparing a server's
50
+ latency to an absolute threshold, HuddleCluster compares each server to the cluster-wide
51
+ median. A server 3x slower than its peers is evicted regardless of whether the baseline is
52
+ 10 ms or 300 ms.
53
+
54
+ ---
55
+
56
+ ## Benchmark Results
57
+
58
+ ### Simulated Benchmark (10 trials, mean +/- std, Welch's t-test)
59
+
60
+ | Scenario / Metric | Round Robin | Least Conn | HuddleCluster | p-value |
61
+ |---|---|---|---|---|
62
+ | **Normal Load** | | | | |
63
+ | P50 (ms) | 21.5 +/- 0.2 | 21.2 +/- 0.3 | 21.0 +/- 0.2 | 0.000* |
64
+ | P95 (ms) | 29.6 +/- 0.3 | 28.8 +/- 0.4 | 28.6 +/- 0.6 | 0.001* |
65
+ | Avg (ms) | 21.4 +/- 0.1 | 21.1 +/- 0.2 | 21.0 +/- 0.2 | 0.000* |
66
+ | Fairness (Gini) | 0.000 | 0.067 | **0.000** | -- |
67
+ | **Slow Server (5x at halfway)** | | | | |
68
+ | P95 (ms) | 63.2 +/- 1.0 | 61.7 +/- 1.1 | 55.1 +/- 10.6 | 0.039* |
69
+ | Avg (ms) | 20.1 +/- 0.2 | 19.7 +/- 0.2 | 19.6 +/- 0.4 | 0.002* |
70
+ | **Server Failure (crash at halfway)** | | | | |
71
+ | P95 (ms) | 500.0 +/- 0.0 | 500.0 +/- 0.0 | **23.9 +/- 0.5** | 0.000* |
72
+ | Avg (ms) | 53.4 +/- 0.2 | 229.7 +/- 1.4 | **29.7 +/- 0.1** | 0.000* |
73
+
74
+ *statistically significant (p < 0.05)*
75
+
76
+ ### Real HTTP Benchmark (6 FastAPI servers, loopback)
77
+
78
+ | Scenario / Metric | Round Robin | Least Conn | HuddleCluster | vs RR |
79
+ |---|---|---|---|---|
80
+ | **Normal Load** | | | | |
81
+ | P95 (ms) | 88.6 | 85.3 | 74.3 | +16.2% |
82
+ | Avg (ms) | 51.8 | 48.3 | 46.1 | +11.0% |
83
+ | **Slow Server (5x)** | | | | |
84
+ | Avg (ms) | 55.2 | 52.1 | 53.4 | +3.4% |
85
+ | **Server Failure** | | | | |
86
+ | P95 (ms) | 5,026.9 | 5,027.9 | **85.6** | +98.3% |
87
+ | Avg (ms) | 429.7 | 414.0 | **181.5** | +57.7% |
88
+
89
+ ### Industry Baseline (NGINX vs HuddleCluster, Docker)
90
+
91
+ Containerised benchmark: 6 FastAPI upstream servers, Docker bridge network,
92
+ NGINX round-robin and NGINX least-connections as baselines.
93
+
94
+ | Scenario / Metric | NGINX RR | NGINX LC | HuddleCluster | vs NGINX RR |
95
+ |---|---|---|---|---|
96
+ | **Normal Load** | | | | |
97
+ | P50 (ms) | 28.4 | 27.5 | **20.5** | +28.0% |
98
+ | P95 (ms) | 55.1 | 39.3 | **33.4** | +39.4% |
99
+ | Avg (ms) | 29.1 | 26.4 | **21.4** | +26.5% |
100
+ | **Slow Server (5x)** | | | | |
101
+ | P50 (ms) | 25.3 | 25.3 | **19.8** | +21.6% |
102
+ | P95 (ms) | 38.9 | 42.8 | **33.6** | +13.6% |
103
+ | Avg (ms) | 25.1 | 25.8 | **20.5** | +18.4% |
104
+ | **Server Failure** | | | | |
105
+ | P95 (ms) | 45.9 | 41.9 | **29.7** | +35.3% |
106
+ | Avg (ms) | 25.9 | 25.6 | **20.8** | +19.4% |
107
+
108
+ Note: admin endpoint injection was not available in this Docker run
109
+ (upstream servers on internal network only). Results reflect
110
+ HuddleCluster's thermal rotation advantage without injected failures.
111
+
112
+ ```bash
113
+ cd benchmarks/
114
+ docker compose up -d --build
115
+ python benchmark_industry.py
116
+ docker compose down
117
+ ```
118
+
119
+ ### Overhead
120
+
121
+ | Measurement | Value |
122
+ |---|---|
123
+ | RR get_server() | 0.277 us |
124
+ | HC get_server() | 0.295 us (1.07x over RR) |
125
+ | HC get_server() + record_latency() | 10.7 us |
126
+ | Peak memory (20 servers) | 28.3 KB |
127
+ | Slow-server detection speed | 36 requests avg (range 35-40) |
128
+
129
+ ---
130
+
131
+ ## Quick Start
132
+
133
+ ```bash
134
+ pip install -e .
135
+ # with benchmark dependencies:
136
+ pip install -e ".[benchmark]"
137
+ # with FastAPI integration:
138
+ pip install -e ".[fastapi]"
139
+ ```
140
+
141
+ ```python
142
+ from huddle_cluster import create_cluster
143
+ import time, requests
144
+
145
+ cluster = create_cluster([
146
+ ("s1", "10.0.0.1", 8080),
147
+ ("s2", "10.0.0.2", 8080),
148
+ ("s3", "10.0.0.3", 8080),
149
+ ])
150
+ cluster.start()
151
+
152
+ # Route a request with latency feedback
153
+ server = cluster.get_server()
154
+ t0 = time.perf_counter()
155
+ response = requests.get(f"http://{server.host}:{server.port}/api")
156
+ cluster.record_latency(server, (time.perf_counter() - t0) * 1000)
157
+
158
+ # Or use the context manager (auto-records latency)
159
+ with cluster.get_server_context() as server:
160
+ response = requests.get(f"http://{server.host}:{server.port}/api")
161
+
162
+ print(cluster.health_report())
163
+ cluster.stop()
164
+ ```
165
+
166
+ ---
167
+
168
+ ## v1.3.0 Features
169
+
170
+ ### Weighted Server Capacity
171
+
172
+ Servers with higher weight tolerate more load before eviction. Useful for
173
+ heterogeneous clusters where some instances are larger than others.
174
+
175
+ ```python
176
+ cluster = create_cluster([
177
+ ("s1", "10.0.0.1", 8080), # weight=1.0 (default)
178
+ ("s2", "10.0.0.2", 8080, 2.0), # weight=2.0 -- needs 2x heat to evict
179
+ ("s3", "10.0.0.3", 8080, 0.5), # weight=0.5 -- evicts sooner
180
+ ])
181
+ ```
182
+
183
+ ### Cold Start Protection
184
+
185
+ New servers warm up in the outer ring before handling traffic. Prevents
186
+ request spikes on fresh instances that have not yet warmed their caches
187
+ or JIT compilers.
188
+
189
+ ```python
190
+ cluster = HuddleCluster(cold_start_sec=30.0)
191
+ # Any server added will stay in outer ring for 30 seconds
192
+ # regardless of force_inner=True
193
+ ```
194
+
195
+ ### Absolute Latency Floor
196
+
197
+ Guards against majority degradation where the relative anomaly score breaks
198
+ down (when the cluster median itself rises above acceptable levels).
199
+
200
+ ```python
201
+ cluster = HuddleCluster(absolute_latency_floor_ms=500.0)
202
+ # Any server with avg latency > 500ms is evicted regardless of relative score
203
+ ```
204
+
205
+ ### Adaptive Thresholds
206
+
207
+ Heat and cool thresholds auto-adjust based on cluster P95 latency history.
208
+ Thresholds loosen under sustained load (to avoid over-eviction) and tighten
209
+ when the cluster is healthy (for faster anomaly detection).
210
+
211
+ ```python
212
+ cluster = HuddleCluster(adaptive_thresholds=True)
213
+ # heat_threshold and cool_threshold update automatically
214
+ # Check current values via cluster.health_report()["heat_threshold"]
215
+ ```
216
+
217
+ ### Prometheus Metrics Exporter
218
+
219
+ Expose cluster state as Prometheus metrics for Grafana dashboards.
220
+
221
+ ```python
222
+ # FastAPI example
223
+ from fastapi import FastAPI
224
+ from fastapi.responses import PlainTextResponse
225
+
226
+ app = FastAPI()
227
+
228
+ @app.get("/metrics", response_class=PlainTextResponse)
229
+ def metrics():
230
+ return cluster.prometheus_metrics()
231
+ ```
232
+
233
+ Metrics exposed: `huddle_server_temperature`, `huddle_server_avg_latency_ms`,
234
+ `huddle_server_anomaly_score`, `huddle_server_rotations_total`,
235
+ `huddle_cluster_inner_count`, `huddle_cluster_fairness_gini`,
236
+ `huddle_cluster_heat_threshold`, `huddle_cluster_p95_latency_ms`.
237
+
238
+ ### Gossip Protocol (Distributed Deployments)
239
+
240
+ Share temperature state between multiple HuddleCluster instances via UDP
241
+ multicast. Each node broadcasts its inner-ring server states; peers receive
242
+ them as advisory signals.
243
+
244
+ ```python
245
+ from huddle_cluster import GossipAgent, create_cluster
246
+
247
+ agent = GossipAgent(node_id="node-1", gossip_port=9999)
248
+ cluster = create_cluster([...], gossip_agent=agent)
249
+ cluster.start()
250
+
251
+ # See peer states
252
+ peers = agent.peer_states()
253
+ # {"node-2": [{"id": "s0", "temp": 0.12, "avg_ms": 15.3, "pos": "inner"}]}
254
+ ```
255
+
256
+ Note: gossip is best-effort UDP multicast. The cluster remains fully
257
+ functional without gossip -- it is purely additive.
258
+
259
+ ---
260
+
261
+ ## File Structure
262
+
263
+ ```
264
+ HuddleCluster/
265
+ |
266
+ |-- huddle_cluster.py # Core library v1.3.0 (zero runtime dependencies)
267
+ |-- __init__.py # Package exports
268
+ |-- pyproject.toml # pip install support
269
+ |-- requirements.txt # Optional dependencies by feature
270
+ |-- LICENSE
271
+ |
272
+ |-- benchmarks/
273
+ | |-- benchmark.py # Simulated 4-scenario benchmark
274
+ | |-- benchmark_statistical.py # 10-trial statistical benchmark with CI
275
+ | |-- benchmark_http.py # Real HTTP benchmark (6 FastAPI servers)
276
+ | |-- benchmark_industry.py # NGINX vs HuddleCluster (Docker)
277
+ | |-- upstream_server.py # FastAPI upstream server
278
+ | |-- docker-compose.yml # 6 upstream servers + 2 NGINX instances
279
+ | |-- nginx/
280
+ | | |-- nginx_rr.conf # NGINX round-robin config
281
+ | | |-- nginx_lc.conf # NGINX least-connections config
282
+ | |-- run_http_benchmark.bat # Windows one-click runner
283
+ |
284
+ |-- tests/
285
+ | |-- test_rotation.py # Rotation, eviction, feedback loop (45 tests)
286
+ | |-- test_fairness.py # Fairness and Gini tests
287
+ | |-- test_stress.py # Concurrent load tests
288
+ | |-- conftest.py # Shared fixtures
289
+ |
290
+ |-- examples/
291
+ | |-- fastapi_example.py # FastAPI reverse proxy integration
292
+ | |-- simulation.py # Terminal simulation
293
+ | |-- HuddleSimulation.jsx # React visual simulation
294
+ |
295
+ |-- docs/
296
+ |-- diagrams/
297
+ |-- architecture_diagram.png # Dual-ring architecture
298
+ |-- temperature_lifecycle.png # State machine + weight composition
299
+ |-- rotation_flowchart.png # Rotation algorithm flowchart
300
+ |-- generate_diagrams.py # Regenerate diagrams
301
+ ```
302
+
303
+ ---
304
+
305
+ ## How It Works
306
+
307
+ ### Temperature Formula
308
+
309
+ ```
310
+ raw(s) = 0.70 x anomaly(s) # relative latency vs cluster median
311
+ + 0.10 x cpu(s) # CPU usage [0,1]
312
+ + 0.10 x conn(s) # active connections / 1000, clamped [0,1]
313
+ + 0.05 x mem(s) # memory usage [0,1]
314
+ + 0.05 x err(s) # error rate [0,1]
315
+
316
+ T(s) = alpha x raw(s) + (1 - alpha) x T(s) [EMA, default alpha=0.60]
317
+ ```
318
+
319
+ ### Relative Latency Anomaly
320
+
321
+ ```
322
+ anomaly(s) = clamp( (avg_ms(s) / median_ms(inner_ring) - 1) / 2, 0, 1 )
323
+ ```
324
+
325
+ | Server / Cluster Median | Ratio | Anomaly Score | Cycles to eviction |
326
+ |---|---|---|---|
327
+ | 12 ms / 12 ms | 1.0x (normal) | 0.00 | Never |
328
+ | 24 ms / 12 ms | 2.0x (warm) | 0.50 | ~8 cycles |
329
+ | 36 ms / 12 ms | 3.0x (hot) | 1.00 | ~3 cycles |
330
+ | 60 ms / 12 ms | 5.0x (degraded) | 1.00 (clamped) | ~3 cycles |
331
+
332
+ ### Rotation Rules
333
+
334
+ 1. **Eviction** — inner server with T >= 0.55 moves to outer ring. Capped at max(1, |inner|/3) per cycle (thundering-herd prevention).
335
+ 2. **Promotion** — coolest outer server with T <= 0.30 and sufficient dwell time moves to inner ring (flapping prevention).
336
+ 3. **Health eviction** — server with is_healthy=False is evicted immediately regardless of temperature.
337
+ 4. **Emergency fallback** — if inner ring drops below min_inner, the globally coolest server is promoted unconditionally.
338
+
339
+ ### Failure-Mode Bounds
340
+
341
+ **Median robustness**: up to floor((n-1)/2) simultaneous server degradations can be
342
+ detected correctly. If k >= n/2 servers degrade simultaneously, the median baseline rises
343
+ and anomaly detection weakens — a documented boundary condition.
344
+
345
+ **Oscillation bound**: a server cannot oscillate faster than
346
+ `rotation_cooldown_sec + min_outer_dwell_sec` per cycle (default: 15 seconds minimum).
347
+ EMA smoothing requires at least 20 consecutive anomalous readings before a healthy server
348
+ (raw < 0.10) is evicted.
349
+
350
+ **Worst-case eviction rate**: at most max(1, floor(|inner|/3)) evictions per rotation
351
+ cycle. With default settings, the inner ring never drops below min_inner=2 active servers.
352
+
353
+ ---
354
+
355
+ ## Configuration
356
+
357
+ ```python
358
+ cluster = HuddleCluster(
359
+ heat_threshold = 0.55, # Evict above this temperature
360
+ cool_threshold = 0.30, # Promote below this temperature
361
+ min_inner_size = 2, # Minimum active servers
362
+ max_inner_size = 5, # Maximum active servers
363
+ rotation_cooldown_sec = 5.0, # Minimum seconds between evictions per server
364
+ min_outer_dwell_sec = 10.0, # Minimum rest time before re-entry
365
+ ema_alpha = 0.60, # Temperature smoothing (higher = faster reaction)
366
+ # v1.3.0 new parameters
367
+ absolute_latency_floor_ms = None, # Evict any server above this absolute latency
368
+ cold_start_sec = 0.0, # New servers warm up in outer ring for this long
369
+ adaptive_thresholds = False, # Auto-adjust thresholds from cluster P95 history
370
+ gossip_agent = None, # GossipAgent for distributed deployments
371
+ metrics_updater = None, # Optional: fn(server) -> updates server.metrics
372
+ on_rotation = None, # Optional: fn(RotationEvent) -> called on rotation
373
+ )
374
+ ```
375
+
376
+ ### Parameter Sensitivity (P95 ms, slow-server scenario)
377
+
378
+ | heat_threshold \ alpha | alpha=0.3 | alpha=0.6 (default) | alpha=0.9 |
379
+ |---|---|---|---|
380
+ | 0.45 (aggressive) | 38.2 | 31.4 | 29.1 |
381
+ | **0.55 (default)** | 52.3 | **32.0** | 30.8 |
382
+ | 0.65 (conservative) | 74.1 | 58.6 | 41.2 |
383
+
384
+ Default (heat=0.55, alpha=0.60) balances detection speed and eviction stability.
385
+
386
+ ---
387
+
388
+ ## Running Tests
389
+
390
+ ```bash
391
+ # All 45 tests
392
+ python -m unittest tests/test_rotation.py tests/test_fairness.py tests/test_stress.py
393
+
394
+ # With pytest
395
+ pip install ".[dev]"
396
+ pytest tests/ -v
397
+ ```
398
+
399
+ ---
400
+
401
+ ## Running Benchmarks
402
+
403
+ ```bash
404
+ cd benchmarks/
405
+
406
+ # Simulated (4 scenarios, ~2 min)
407
+ python benchmark.py
408
+
409
+ # Statistical (10 trials, p-values, CI, ~6 min)
410
+ pip install scipy matplotlib numpy
411
+ python benchmark_statistical.py
412
+
413
+ # Real HTTP (6 FastAPI servers, ~3 min)
414
+ pip install fastapi uvicorn httpx matplotlib numpy
415
+ python benchmark_http.py # Linux/Mac
416
+ run_http_benchmark.bat # Windows
417
+
418
+ # Industry baseline: NGINX vs HuddleCluster (requires Docker)
419
+ docker compose up -d
420
+ python benchmark_industry.py
421
+ docker compose down
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Known Limitations
427
+
428
+ - **Uniform burst load**: when all servers are equally stressed, relative anomaly scores are near zero and no eviction fires. An absolute latency floor is planned.
429
+ - **Majority degradation**: if more than half the inner-ring servers degrade simultaneously, the median baseline rises. Use `absolute_latency_floor_ms` as a secondary guard in this scenario.
430
+ - **Single-process**: temperature state is not shared across hosts. A gossip-protocol extension is planned.
431
+ - **Loopback benchmarks**: all HTTP benchmarks use localhost. Wide-area production validation is future work.
432
+
433
+ ---
434
+
435
+ ## Roadmap
436
+
437
+ - [x] Latency feedback loop (record_latency, get_server_context) — v1.1.0
438
+ - [x] Relative latency anomaly scoring (median baseline) — v1.2.0
439
+ - [x] Inner-ring fairness metric (Gini) — v1.2.0
440
+ - [x] Tunable EMA alpha — v1.2.0
441
+ - [x] Statistical benchmark (10 trials, Welch's t-test, 95% CI) — v1.2.0
442
+ - [x] Real HTTP benchmark (FastAPI upstream servers) — v1.2.0
443
+ - [x] Industry baseline benchmark (NGINX, Docker) — v1.2.0
444
+ - [x] Failure-mode bounds (median robustness, oscillation, eviction rate) — v1.2.0
445
+ - [x] Adaptive thresholds (auto-adjust heat/cool from cluster P95 history) -- v1.3.0
446
+ - [x] Weighted server capacity (weight= param on Server/create_cluster) -- v1.3.0
447
+ - [x] Cold start protection (cold_start_sec= param) -- v1.3.0
448
+ - [x] Prometheus metrics exporter (cluster.prometheus_metrics()) -- v1.3.0
449
+ - [x] Distributed temperature sharing (GossipAgent, UDP multicast) -- v1.3.0
450
+ - [x] Absolute latency floor (absolute_latency_floor_ms= param) -- v1.3.0
451
+
452
+ ---
453
+
454
+ ## Citation
455
+
456
+ ```
457
+ Bhuiya, R. (2025). HuddleCluster: A Penguin-Inspired Self-Organizing Load Balancer
458
+ with Adaptive Thermal Eviction. https://github.com/rahadbhuiya/HuddleCluster
459
+ ```
460
+
461
+ ---
462
+
463
+ ## License
464
+
465
+ MIT — see LICENSE.
466
+
@@ -0,0 +1,5 @@
1
+ huddle_cluster.py,sha256=Y44l1XHiqsgiruLFEkSsmzKv7cIo80Fw85TzGGvepX8,54323
2
+ huddle_cluster-1.3.1.dist-info/WHEEL,sha256=zwUFtOH-KGODloEEaBQEGpGKFOXEzedEklcD4qr64mI,92
3
+ huddle_cluster-1.3.1.dist-info/METADATA,sha256=PqaRJFZJXcJca9RYxgC1ZmRH2qA9L4wIomYidSKXsnQ,16705
4
+ huddle_cluster-1.3.1.dist-info/LICENSE.txt,,
5
+ huddle_cluster-1.3.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: huddle-cluster-manual
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any