hugpy-fleet 0.2.2__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.
- hugpy_fleet-0.2.2/LICENSE +41 -0
- hugpy_fleet-0.2.2/PKG-INFO +43 -0
- hugpy_fleet-0.2.2/README.md +7 -0
- hugpy_fleet-0.2.2/docs/COMFY-LEDGER.md +87 -0
- hugpy_fleet-0.2.2/docs/README.md +14 -0
- hugpy_fleet-0.2.2/docs/STATE-MODEL.md +270 -0
- hugpy_fleet-0.2.2/docs/WORKER-BOOT-PREWARM.md +135 -0
- hugpy_fleet-0.2.2/docs/WORKER-WILDCARD.md +77 -0
- hugpy_fleet-0.2.2/docs/WORKER-WIREGUARD.md +127 -0
- hugpy_fleet-0.2.2/pyproject.toml +74 -0
- hugpy_fleet-0.2.2/setup.cfg +4 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/__init__.py +53 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/__init__.py +0 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/admission_gate.py +78 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/agent_nodes.py +414 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/archive_gate.py +125 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/blocklist.py +267 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/calibration.py +390 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/config.py +101 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/enrollment_tokens.py +220 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/evict_policy.py +109 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/evictions.py +1007 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/feeds.py +240 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/heartbeat_db.py +201 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/model_groups.py +389 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/model_metrics.py +976 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/oracle_adapters.py +72 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/peers.py +124 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/phone_brick_store.py +330 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/pid_attribution.py +249 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/placement.py +511 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/pool_guard.py +167 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/priority_group_settings.py +269 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/priority_groups.py +518 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/task_templates.py +323 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/wg_join.py +414 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/worker_http.py +434 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/central/workers.py +7502 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/cli.py +30 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/doctrine/__init__.py +69 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/doctrine/doctor.py +602 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/doctrine/doctrine.py +665 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/doctrine/fleet_runbook.json +95 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/doctrine/runbook.py +33 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/fleet_manager/__init__.py +46 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/fleet_manager/templates.py +565 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/gguf_worker/__init__.py +10 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/gguf_worker/__main__.py +4 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/gguf_worker/agent.py +852 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/__init__.py +64 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/__main__.py +135 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/analyze.py +136 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/bootstrap.sh +141 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/client.py +75 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/consensus.py +35 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/detector.py +227 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/http.py +17 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/orchestrator.py +148 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/protocol.py +96 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/registration.py +134 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/rendering.py +43 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/rpc_backend.py +359 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/schemas.py +196 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick/worker.py +244 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick_orchestrator/__init__.py +11 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/phone_brick_orchestrator/runner.py +141 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/py.typed +0 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/toks_report.py +87 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/wire.py +181 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/__init__.py +7 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/__main__.py +4 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/_studio_subproc.py +300 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/agent.py +13360 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aggregate.py +596 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aptitude/__init__.py +15 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aptitude/cases.py +364 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aptitude/parse.py +73 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aptitude/score.py +401 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/aptitude/selftest.py +227 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/bootstrap.sh +267 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/budget.py +835 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/comfy_ledger.py +221 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/comfy_process.py +445 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/comfy_watchdog.py +536 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/deploy/install.sh +54 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/environment_report.py +589 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/external_residents.py +135 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/flex.py +689 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/gen_gate.py +323 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/gpu_lease.py +594 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/imports.py +74 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/install.py +606 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/logs.py +233 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/pid_registry.py +601 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/plugins.py +196 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/setup.py +1032 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/slot_child.py +26 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/storage_hooks.py +68 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/studio_render.py +363 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/studio_reserve.py +222 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet/worker/wg_join.sh +185 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/PKG-INFO +43 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/SOURCES.txt +221 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/dependency_links.txt +1 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/entry_points.txt +5 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/requires.txt +14 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/scm_file_list.json +217 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/scm_version.json +8 -0
- hugpy_fleet-0.2.2/src/hugpy_fleet.egg-info/top_level.txt +1 -0
- hugpy_fleet-0.2.2/tests/conftest.py +24 -0
- hugpy_fleet-0.2.2/tests/test_admission_gate.py +41 -0
- hugpy_fleet-0.2.2/tests/test_alloc_scope_pair_block.py +182 -0
- hugpy_fleet-0.2.2/tests/test_allocation_state_honesty.py +250 -0
- hugpy_fleet-0.2.2/tests/test_allocations_resident_bytes.py +192 -0
- hugpy_fleet-0.2.2/tests/test_assignment_memory_forget.py +45 -0
- hugpy_fleet-0.2.2/tests/test_attributed_vs_resident.py +135 -0
- hugpy_fleet-0.2.2/tests/test_bar_summary_central.py +128 -0
- hugpy_fleet-0.2.2/tests/test_block_propagation.py +140 -0
- hugpy_fleet-0.2.2/tests/test_bootstrap_dry_run.py +44 -0
- hugpy_fleet-0.2.2/tests/test_budget_min_wins.py +303 -0
- hugpy_fleet-0.2.2/tests/test_budget_reserve_carveout.py +195 -0
- hugpy_fleet-0.2.2/tests/test_bulk_reap_assigned_candidate.py +201 -0
- hugpy_fleet-0.2.2/tests/test_call_metrics_by_task.py +97 -0
- hugpy_fleet-0.2.2/tests/test_calls_by_caller.py +59 -0
- hugpy_fleet-0.2.2/tests/test_central_task_gating.py +143 -0
- hugpy_fleet-0.2.2/tests/test_comfy_checkpoint_routing.py +223 -0
- hugpy_fleet-0.2.2/tests/test_comfy_cuda_device.py +77 -0
- hugpy_fleet-0.2.2/tests/test_comfy_headroom.py +231 -0
- hugpy_fleet-0.2.2/tests/test_comfy_idle_stop.py +151 -0
- hugpy_fleet-0.2.2/tests/test_comfy_idle_watchdog.py +556 -0
- hugpy_fleet-0.2.2/tests/test_comfy_ledger.py +338 -0
- hugpy_fleet-0.2.2/tests/test_comfy_ondemand_start.py +91 -0
- hugpy_fleet-0.2.2/tests/test_comfy_process.py +188 -0
- hugpy_fleet-0.2.2/tests/test_dir_size_effective.py +156 -0
- hugpy_fleet-0.2.2/tests/test_emfile_retry.py +259 -0
- hugpy_fleet-0.2.2/tests/test_entry_points.py +36 -0
- hugpy_fleet-0.2.2/tests/test_environment_report_build.py +85 -0
- hugpy_fleet-0.2.2/tests/test_evict_policy_knobs.py +432 -0
- hugpy_fleet-0.2.2/tests/test_eviction_aware_autofit.py +482 -0
- hugpy_fleet-0.2.2/tests/test_eviction_parity.py +275 -0
- hugpy_fleet-0.2.2/tests/test_explicit_exact_fit.py +92 -0
- hugpy_fleet-0.2.2/tests/test_external_gpu_lease.py +380 -0
- hugpy_fleet-0.2.2/tests/test_feasible_distribution.py +154 -0
- hugpy_fleet-0.2.2/tests/test_fetch_to_disk.py +108 -0
- hugpy_fleet-0.2.2/tests/test_fleet_config.py +41 -0
- hugpy_fleet-0.2.2/tests/test_flex_bands.py +185 -0
- hugpy_fleet-0.2.2/tests/test_flex_evict_integration.py +169 -0
- hugpy_fleet-0.2.2/tests/test_free_ram_ops.py +102 -0
- hugpy_fleet-0.2.2/tests/test_gguf_worker_pin.py +199 -0
- hugpy_fleet-0.2.2/tests/test_grant_marker.py +189 -0
- hugpy_fleet-0.2.2/tests/test_hot_root_degenerate.py +222 -0
- hugpy_fleet-0.2.2/tests/test_imagegen_headroom.py +185 -0
- hugpy_fleet-0.2.2/tests/test_import_policy.py +107 -0
- hugpy_fleet-0.2.2/tests/test_incoming_need_gguf_effective.py +79 -0
- hugpy_fleet-0.2.2/tests/test_infer_key_and_slot_identity.py +301 -0
- hugpy_fleet-0.2.2/tests/test_kv_context_allocation.py +348 -0
- hugpy_fleet-0.2.2/tests/test_lazy_download_assignment.py +265 -0
- hugpy_fleet-0.2.2/tests/test_load_bytes_per_s.py +47 -0
- hugpy_fleet-0.2.2/tests/test_load_metrics_producer.py +69 -0
- hugpy_fleet-0.2.2/tests/test_model_groups.py +487 -0
- hugpy_fleet-0.2.2/tests/test_model_groups_offpath.py +246 -0
- hugpy_fleet-0.2.2/tests/test_model_metrics.py +102 -0
- hugpy_fleet-0.2.2/tests/test_moe_placement.py +2118 -0
- hugpy_fleet-0.2.2/tests/test_no_worker_diagnostic.py +244 -0
- hugpy_fleet-0.2.2/tests/test_oracle_adapters.py +65 -0
- hugpy_fleet-0.2.2/tests/test_orphan_scan.py +248 -0
- hugpy_fleet-0.2.2/tests/test_partial_offload.py +168 -0
- hugpy_fleet-0.2.2/tests/test_per_call_metrics.py +104 -0
- hugpy_fleet-0.2.2/tests/test_per_device_evict.py +106 -0
- hugpy_fleet-0.2.2/tests/test_per_device_placement.py +181 -0
- hugpy_fleet-0.2.2/tests/test_pid_registry.py +355 -0
- hugpy_fleet-0.2.2/tests/test_placement_adapters.py +198 -0
- hugpy_fleet-0.2.2/tests/test_pool_guard.py +159 -0
- hugpy_fleet-0.2.2/tests/test_priority_group_workers.py +241 -0
- hugpy_fleet-0.2.2/tests/test_provision_chain_of_command.py +300 -0
- hugpy_fleet-0.2.2/tests/test_provision_concurrency_gate.py +194 -0
- hugpy_fleet-0.2.2/tests/test_provisioning_liveness.py +248 -0
- hugpy_fleet-0.2.2/tests/test_reap_gpu_orphans.py +366 -0
- hugpy_fleet-0.2.2/tests/test_reap_store_root_classification.py +213 -0
- hugpy_fleet-0.2.2/tests/test_residency_contention.py +329 -0
- hugpy_fleet-0.2.2/tests/test_residency_materialized.py +215 -0
- hugpy_fleet-0.2.2/tests/test_residency_static.py +362 -0
- hugpy_fleet-0.2.2/tests/test_route_capacity.py +184 -0
- hugpy_fleet-0.2.2/tests/test_route_rank.py +629 -0
- hugpy_fleet-0.2.2/tests/test_runbook_resource.py +21 -0
- hugpy_fleet-0.2.2/tests/test_serve_metrics_ledger.py +657 -0
- hugpy_fleet-0.2.2/tests/test_slot_vram_ceiling.py +376 -0
- hugpy_fleet-0.2.2/tests/test_storage_budget.py +319 -0
- hugpy_fleet-0.2.2/tests/test_storage_budget_fifo.py +1054 -0
- hugpy_fleet-0.2.2/tests/test_storage_hooks.py +76 -0
- hugpy_fleet-0.2.2/tests/test_storage_shared_store_accounting.py +339 -0
- hugpy_fleet-0.2.2/tests/test_studio_presence_root.py +123 -0
- hugpy_fleet-0.2.2/tests/test_studio_render_watchdog.py +156 -0
- hugpy_fleet-0.2.2/tests/test_task_templates.py +138 -0
- hugpy_fleet-0.2.2/tests/test_toks_report.py +249 -0
- hugpy_fleet-0.2.2/tests/test_vram_evict_to_fit.py +1184 -0
- hugpy_fleet-0.2.2/tests/test_vram_holders.py +299 -0
- hugpy_fleet-0.2.2/tests/test_vram_squatters_central.py +133 -0
- hugpy_fleet-0.2.2/tests/test_wg_join.py +196 -0
- hugpy_fleet-0.2.2/tests/test_wire_dtos.py +41 -0
- hugpy_fleet-0.2.2/tests/test_worker_boot_prewarm.py +24 -0
- hugpy_fleet-0.2.2/tests/test_worker_converge.py +367 -0
- hugpy_fleet-0.2.2/tests/test_worker_env_profiles.py +376 -0
- hugpy_fleet-0.2.2/tests/test_worker_gen_gate.py +205 -0
- hugpy_fleet-0.2.2/tests/test_worker_hot_cache_root.py +199 -0
- hugpy_fleet-0.2.2/tests/test_worker_http_discipline.py +392 -0
- hugpy_fleet-0.2.2/tests/test_worker_install.py +149 -0
- hugpy_fleet-0.2.2/tests/test_worker_load_contract.py +33 -0
- hugpy_fleet-0.2.2/tests/test_worker_load_failure_payload.py +32 -0
- hugpy_fleet-0.2.2/tests/test_worker_plugins.py +102 -0
- hugpy_fleet-0.2.2/tests/test_worker_presence_config_learning.py +271 -0
- hugpy_fleet-0.2.2/tests/test_worker_probe_spill.py +95 -0
- hugpy_fleet-0.2.2/tests/test_worker_reject_marker.py +44 -0
- hugpy_fleet-0.2.2/tests/test_worker_rename_migration.py +154 -0
- hugpy_fleet-0.2.2/tests/test_worker_restart.py +287 -0
- hugpy_fleet-0.2.2/tests/test_worker_serving_fields.py +126 -0
- hugpy_fleet-0.2.2/tests/test_worker_setup.py +138 -0
- hugpy_fleet-0.2.2/tests/test_worker_store_isolation.py +79 -0
- hugpy_fleet-0.2.2/tests/test_worker_supersede.py +196 -0
- hugpy_fleet-0.2.2/tests/test_worker_task_capabilities.py +186 -0
- hugpy_fleet-0.2.2/tests/test_worker_version_honesty.py +143 -0
- hugpy_fleet-0.2.2/tests/test_worker_wildcard.py +163 -0
- hugpy_fleet-0.2.2/tests/worker_store_isolation.py +47 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
hugpy — Source-Available License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 putkoff (hugpy.ai). All rights reserved.
|
|
4
|
+
|
|
5
|
+
Permission is granted, free of charge, to use this software ("hugpy") for
|
|
6
|
+
personal and non-commercial purposes, and for time-limited commercial
|
|
7
|
+
evaluation, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
1. Non-commercial use means use by an individual for personal purposes, or
|
|
10
|
+
use by a non-profit or educational institution for its own internal
|
|
11
|
+
purposes. Any use by, for, or on behalf of a for-profit business or in
|
|
12
|
+
connection with revenue-generating activity is commercial use — including
|
|
13
|
+
internal business use, use in producing goods or services, and use on
|
|
14
|
+
paid engagements.
|
|
15
|
+
|
|
16
|
+
2. Commercial use requires a commercial license from the copyright holder.
|
|
17
|
+
Exception: a business may evaluate the software internally for up to
|
|
18
|
+
thirty (30) days free of charge; continued use after that requires a
|
|
19
|
+
commercial license.
|
|
20
|
+
|
|
21
|
+
3. Redistribution of this software, in source or binary form, modified or
|
|
22
|
+
unmodified, is not permitted without prior written permission from the
|
|
23
|
+
copyright holder. Downloading the software from an official distribution
|
|
24
|
+
channel (PyPI, npm, hugpy.ai) is not redistribution.
|
|
25
|
+
|
|
26
|
+
4. Modification for personal use or internal evaluation is permitted;
|
|
27
|
+
distribution of modified versions is not.
|
|
28
|
+
|
|
29
|
+
5. This notice must be retained in all copies or substantial portions of
|
|
30
|
+
the software.
|
|
31
|
+
|
|
32
|
+
6. Any use outside these terms automatically terminates this license.
|
|
33
|
+
|
|
34
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING
|
|
38
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
39
|
+
IN THE SOFTWARE.
|
|
40
|
+
|
|
41
|
+
For commercial licensing or redistribution permission: https://hugpy.ai
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hugpy-fleet
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Hugpy fleet: central worker registry, enrollment, heartbeats, evictions, doctrine and placement, plus the full, GGUF and phone worker agents
|
|
5
|
+
Author-email: putkoff <support@hugpy.ai>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://hugpy.ai
|
|
8
|
+
Project-URL: Documentation, https://github.com/hugpy/hugpy/blob/main/py/fleet/hugpy_fleet/README.md
|
|
9
|
+
Project-URL: Repository, https://github.com/hugpy/hugpy
|
|
10
|
+
Project-URL: Source, https://github.com/hugpy/hugpy/tree/main/py/fleet/hugpy_fleet
|
|
11
|
+
Project-URL: Issues, https://github.com/hugpy/hugpy/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/hugpy/hugpy/releases
|
|
13
|
+
Project-URL: Architecture, https://github.com/hugpy/hugpy/blob/main/PARTITION.md
|
|
14
|
+
Keywords: hugpy,llm,self-hosted,gpu,fleet,workers,distributed-inference,rpc-sharding
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: hugpy-platform
|
|
25
|
+
Requires-Dist: hugpy-control
|
|
26
|
+
Requires-Dist: hugpy-storage
|
|
27
|
+
Requires-Dist: hugpy-engine
|
|
28
|
+
Requires-Dist: flask>=3
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
31
|
+
Provides-Extra: media
|
|
32
|
+
Requires-Dist: hugpy-media; extra == "media"
|
|
33
|
+
Provides-Extra: video
|
|
34
|
+
Requires-Dist: hugpy-video; extra == "video"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# hugpy-fleet
|
|
38
|
+
|
|
39
|
+
`hugpy_fleet` — extracted from `abstract_hugpy_dev` as part of the Hugpy
|
|
40
|
+
partition. Ownership and allowed dependencies are declared in
|
|
41
|
+
`py/partition.toml`; see `PARTITION.md` at the workspace root.
|
|
42
|
+
|
|
43
|
+
Allowed Python dependencies inside the ecosystem: hugpy_platform, hugpy_control, hugpy_storage, hugpy_engine.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# hugpy-fleet
|
|
2
|
+
|
|
3
|
+
`hugpy_fleet` — extracted from `abstract_hugpy_dev` as part of the Hugpy
|
|
4
|
+
partition. Ownership and allowed dependencies are declared in
|
|
5
|
+
`py/partition.toml`; see `PARTITION.md` at the workspace root.
|
|
6
|
+
|
|
7
|
+
Allowed Python dependencies inside the ecosystem: hugpy_platform, hugpy_control, hugpy_storage, hugpy_engine.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# The ComfyUI resident ledger
|
|
2
|
+
|
|
3
|
+
**Since:** 2026-09-22. **Code:** `hugpy_fleet/worker/comfy_ledger.py` (pure),
|
|
4
|
+
bound in `hugpy_fleet/worker/agent.py` (`_comfy_ledger`, `_comfy_need_detail`,
|
|
5
|
+
`_comfy_headroom_target`, `_comfy_busy_reason`). **Tests:**
|
|
6
|
+
`tests/test_comfy_ledger.py`, `tests/test_vram_evict_to_fit.py`,
|
|
7
|
+
`tests/test_comfy_headroom.py`.
|
|
8
|
+
|
|
9
|
+
## The problem it closes
|
|
10
|
+
|
|
11
|
+
ComfyUI is an external process. nvidia-smi shows one VRAM lump under its PID
|
|
12
|
+
and nothing says which checkpoints are inside it, so the worker's VRAM budget
|
|
13
|
+
never had a per-occupant figure for comfy — which meant three loops with three
|
|
14
|
+
different targets and no shared notion of *need*:
|
|
15
|
+
|
|
16
|
+
| Loop | What it did | Blind spot |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| eviction planner (`_partition_residents`) | ranks measured residents LRU and evicts the minimum set for a load | comfy rows were **always protected** (0.1.137 "out of allocations") |
|
|
19
|
+
| Fix B headroom (`_worker_ensure_comfy_headroom`) | evicts managed models before a comfy gen | cleared a **constant** 7 GiB for every gen — 2 GiB SD1.5 and 12 GiB checkpoints alike |
|
|
20
|
+
| idle watchdog (`comfy_watchdog`) | frees an idle comfy after a TTL, or under contention | a side channel the planner could not see or rank |
|
|
21
|
+
|
|
22
|
+
The one thing a budget needs — how much *this* checkpoint takes — was never
|
|
23
|
+
computed, although it is knowable before the load: `ComfyRunner` names the
|
|
24
|
+
checkpoint file in the graph and, for the fp16 safetensors comfy serves,
|
|
25
|
+
weights on disk ≈ weights in VRAM.
|
|
26
|
+
|
|
27
|
+
## What the ledger is
|
|
28
|
+
|
|
29
|
+
`ComfyLedger` records what hugpy has asked comfy to load since comfy's last
|
|
30
|
+
known `/free`: `{model_key, filename, bytes, loaded_at, last_used}`, oldest
|
|
31
|
+
first. Writes happen at the two places the truth changes:
|
|
32
|
+
|
|
33
|
+
* `note_dispatch` — inside the headroom hook, right before the runner POSTs
|
|
34
|
+
the graph; the file is sized by `checkpoint_size_bytes`.
|
|
35
|
+
* `note_freed` — inside `_comfy_free_models` on a 200, which every `/free`
|
|
36
|
+
goes through (watchdog, evict verb, planner).
|
|
37
|
+
|
|
38
|
+
Reads:
|
|
39
|
+
|
|
40
|
+
* `_vram_residents` unions the ledger's rows in as `host_mode="comfy"` with
|
|
41
|
+
per-row bytes **capped at comfy's measured process VRAM**, and only when a
|
|
42
|
+
comfy process actually holds VRAM (a ledger with no process behind it names
|
|
43
|
+
nothing). The registry still contributes the active call's attribution;
|
|
44
|
+
the ledger adds what comfy still caches.
|
|
45
|
+
* `_comfy_status()["resident"]` carries the same rows in the heartbeat, so the
|
|
46
|
+
console's comfy card can show checkpoints by name instead of one lump.
|
|
47
|
+
|
|
48
|
+
## The three behaviour changes
|
|
49
|
+
|
|
50
|
+
1. **Per-checkpoint need.** `_comfy_need_detail` prices a gen as
|
|
51
|
+
`checkpoint file size + gen cushion`, or the cushion alone when comfy
|
|
52
|
+
already holds the checkpoint *and* its measured VRAM covers the weights (a
|
|
53
|
+
ledger claim the device does not back is dropped, never trusted).
|
|
54
|
+
`_comfy_headroom_target` uses that need; when the file cannot be sized the
|
|
55
|
+
legacy constant applies unchanged, and an **explicitly set**
|
|
56
|
+
`HUGPY_COMFY_TARGET_FREE_GIB` is a floor the need never undercuts.
|
|
57
|
+
2. **Comfy is an evictable resident under contention.** `_partition_residents`
|
|
58
|
+
asks `_comfy_busy_reason` once per partition — the watchdog's own predicate
|
|
59
|
+
(a registered comfy call, a non-empty `/queue`, or anything unreadable = busy)
|
|
60
|
+
— and comfy rows are candidates when idle, protected with
|
|
61
|
+
`why="comfy busy: …"` otherwise. `_evict_model`'s comfy branch honours the
|
|
62
|
+
same predicate (unless `force`), because `/free` drops every checkpoint at
|
|
63
|
+
once. The **idle sweep** still never touches comfy; TTL-based reclaim stays
|
|
64
|
+
the watchdog's job.
|
|
65
|
+
3. **Named residents in telemetry** (above).
|
|
66
|
+
|
|
67
|
+
## Knobs
|
|
68
|
+
|
|
69
|
+
| Env | Default | Meaning |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `HUGPY_COMFY_GEN_CUSHION_GIB` | `2.0` | working room on top of the weights (activations, VAE, CLIP, fragmentation); ae recon: SD1.5 at 512² grew the comfy process ~1 GiB during a gen |
|
|
72
|
+
| `HUGPY_COMFY_TARGET_FREE_GIB` | `7.0` (fallback only) | the legacy constant: used when the checkpoint cannot be sized; a floor when set explicitly |
|
|
73
|
+
| `COMFY_CHECKPOINTS_DIR` | `~/ComfyUI/models/checkpoints` | where hugpy symlinks checkpoints for comfy (`hugpy_storage.provision`); first place a name is resolved |
|
|
74
|
+
| `COMFY_CHECKPOINT_DIRS` | unset | `os.pathsep`-separated extra roots — set it to the roots comfy's own `extra_model_paths.yaml` scans on this box so a name resolves to the file comfy will really load |
|
|
75
|
+
|
|
76
|
+
Resolution order for a `ckpt_name`: as given under each root, then by basename
|
|
77
|
+
anywhere below each root (comfy's scan is recursive). Symlinks are followed;
|
|
78
|
+
a dangling link is "not found" (unknown need → legacy target), never 0.
|
|
79
|
+
|
|
80
|
+
## Doctrine kept
|
|
81
|
+
|
|
82
|
+
* Degrade-not-guess: unmeasurable free VRAM, an unsizable file, a failing
|
|
83
|
+
catalog read — each falls back to exactly the previous behaviour.
|
|
84
|
+
* A render is never killed for an LLM load: the busy predicate is the
|
|
85
|
+
watchdog's, and unprovable idleness reads as busy.
|
|
86
|
+
* Comfy's weights are never counted as headroom for another model's admission
|
|
87
|
+
(`_subject_resident_vram_bytes` is unchanged).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# hugpy-fleet docs
|
|
2
|
+
|
|
3
|
+
Canonical copies moved from the monolith's `deploy/` folder (partition 2026-09-22).
|
|
4
|
+
|
|
5
|
+
| Document | Subject | Owner |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `STATE-MODEL.md` | Model state nomenclature: residency ladder, ETG cost model, policy gates, divergence register | fleet (Layers 2–3, registry/heartbeat truth); Layer-1 residency vocabulary is shared with `hugpy-engine`'s eviction planner |
|
|
8
|
+
| `WORKER-WIREGUARD.md` | One-step WireGuard join for a remote worker: the wg-peer helper + sudoers, `hugpy-fleet join-code`, the worker join script, and revocation | fleet |
|
|
9
|
+
| `WORKER-BOOT-PREWARM.md` | The per-worker boot-load star (`boot_prewarm`) | fleet |
|
|
10
|
+
| `WORKER-WILDCARD.md` | The per-worker wildcard routing opt-in | fleet |
|
|
11
|
+
| `COMFY-LEDGER.md` | What ComfyUI holds, by name and size: per-checkpoint headroom need, comfy as an evictable resident under contention, the knobs | fleet |
|
|
12
|
+
|
|
13
|
+
Code paths in these documents refer to the extracted packages
|
|
14
|
+
(`hugpy_fleet/...`, `hugpy_engine/...`).
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Model State Nomenclature — Canonical Reference
|
|
2
|
+
|
|
3
|
+
**Status:** canonical vocabulary (target state). Some producers/consumers do not yet
|
|
4
|
+
conform; every known divergence is listed in the **Divergence Register** below with a
|
|
5
|
+
`file:line` and a resolution. New code MUST use these terms; drift against this doc is a
|
|
6
|
+
bug in the code, not in the doc.
|
|
7
|
+
|
|
8
|
+
**Why this exists:** the same words (`cold`, `hot`, `warm`, `serving`, `resident`,
|
|
9
|
+
`loaded`, `in_flight`) were used for different things in different subsystems, which
|
|
10
|
+
produced real incidents — e.g. an already-on-disk model refused as if it needed a
|
|
11
|
+
multi‑GB download (the cold‑hold `cold_load_capacity` refusal). This doc fixes the
|
|
12
|
+
meanings.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The one rule
|
|
17
|
+
|
|
18
|
+
**A term names exactly one thing on exactly one layer.**
|
|
19
|
+
|
|
20
|
+
- **Layer 1 — Residency state**: *where the weights physically are* (a thermal ladder).
|
|
21
|
+
- **Layer 2 — Cost model (ETG)**: *how long until this candidate produces output* (times).
|
|
22
|
+
- **Layer 3 — Policy / eligibility**: *may we, and is it even resolvable* (boolean gates).
|
|
23
|
+
|
|
24
|
+
State words are nouns for a location. Cost words always carry a time unit (`t_*`, seconds).
|
|
25
|
+
Policy words are adjectives that filter candidates and never enter a cost sum.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Layer 1 — Residency state (the thermal ladder)
|
|
30
|
+
|
|
31
|
+
"Temperature" = **proximity to GPU compute**. The colder a model is, the more work stands
|
|
32
|
+
between it and producing a token. Per `(worker, model, variant)` the model occupies exactly
|
|
33
|
+
one state:
|
|
34
|
+
|
|
35
|
+
| State | Meaning | Physical location | Authoritative signal |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| `UNAVAILABLE` | Not resolvable anywhere — not on the fleet source-of-truth nor a remote origin (HF). Cannot be provisioned. **Off the ladder: no worker delegation.** | nowhere fetchable | `serveable=False` + `unserveable_reason` |
|
|
38
|
+
| `COLD` | Resolvable on the **source-of-truth** array / origin, but **not on this worker's hot drive**. Must be pulled before it can load. | `MODELS_HOME` (`/mnt/llm_storage/models`) / HF only | resolvable **and** NOT in worker `models_local` |
|
|
39
|
+
| `HOT` | Present on this worker's **hot drive** — the box-local NVMe it loads into VRAM from. Downloaded, ready to load. | `HUGPY_HOT_CACHE_ROOT` (box-local NVMe hot-cache) | in worker `models_local` (disk-truth) |
|
|
40
|
+
| `LOADED` | Weights **measured resident** in VRAM, idle (not answering a request right now). | GPU VRAM | heartbeat allocation row `healthy && materialized`, not `serving`/`busy` |
|
|
41
|
+
| `SERVING` | Resident in VRAM **and actively answering** a request (within the active window). | GPU VRAM | allocation row `serving` (busy or last_used within `_SERVING_WINDOW_S`) |
|
|
42
|
+
|
|
43
|
+
### Transitions (each is the unit of work Layer 2 prices)
|
|
44
|
+
|
|
45
|
+
| Transition | From → To | Name | In-flight state | Signal |
|
|
46
|
+
|---|---|---|---|---|
|
|
47
|
+
| Download | COLD → HOT | **pull** (fetch) | `PULLING` | `provisioning` / `provision_progress` |
|
|
48
|
+
| Load | HOT → LOADED | **load** | `LOADING` (heating) | `loading` |
|
|
49
|
+
| Serve | LOADED → SERVING | — (a request arrives) | — | `serving` flips true |
|
|
50
|
+
| VRAM evict | LOADED/SERVING → HOT | **evict** | `EVICTING` | eviction plan; weights remain HOT on the drive |
|
|
51
|
+
| Hot-drive reap | HOT → COLD | **reap** | — | hot-cache FIFO by time-since-called (`hot_cache.py`) |
|
|
52
|
+
|
|
53
|
+
Notes:
|
|
54
|
+
- **evict ≠ reap.** Eviction frees **VRAM** (the model falls back to `HOT`, still on the
|
|
55
|
+
drive — cheap to re-load). Reaping frees **hot-drive space** (the model falls back to
|
|
56
|
+
`COLD`, must be re-pulled). Conflating these is how "cold" came to mean two things.
|
|
57
|
+
- `RESERVED` (a grant/allocation held but **not** measured-resident, `materialized=false`)
|
|
58
|
+
is not a residency state — it is a Layer-3 attribution. See Layer 3.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Layer 2 — Cost model (Estimated Time to Generation)
|
|
63
|
+
|
|
64
|
+
Placement and eviction are **decisions**, and a residency state cannot make a decision — a
|
|
65
|
+
cost can. For each candidate `(worker, model, variant)`:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
ETG = t_pull # >0 only if COLD bytes / download_bw(worker)
|
|
69
|
+
+ t_evict # >0 only if it won't fit cost to evict victim(s) for `need` bytes
|
|
70
|
+
+ t_load # >0 unless LOADED/SERVING upload_time_s EMA (learned per worker-card)
|
|
71
|
+
+ t_queue # wait behind requests already in flight on that worker/slot [MISSING]
|
|
72
|
+
+ t_generate # avg_output_tokens / tok_per_s EMA (learned per worker-card)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Choose the candidate with **min ETG**. Eviction is the dual: a victim's cost is not its
|
|
76
|
+
bytes, it is its **regret** — `P(needed soon) × (its future t_pull + t_load)`.
|
|
77
|
+
|
|
78
|
+
**Wiring status (as of this writing): NOT WIRED.**
|
|
79
|
+
- `estimate_total_time` computes `t_pull + t_evict + t_load + t_generate`
|
|
80
|
+
(`hugpy_fleet/central/model_metrics.py:265-292`) but has **zero callers** — the estimator is orphaned.
|
|
81
|
+
- `t_queue` does not exist yet.
|
|
82
|
+
- Placement (`_pick_worker` → injected provider, `hugpy_engine/resolvers/remote.py:394`) ranks
|
|
83
|
+
by **residency** (`_resident_on`, `hugpy_fleet/central/workers.py:2646`), not ETG.
|
|
84
|
+
- Eviction (`hugpy_engine/eviction.py`) ranks victims by **recency + bytes**, not regret.
|
|
85
|
+
|
|
86
|
+
This doc defines the vocabulary the wiring will use; the wiring itself is a separate change.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Layer 3 — Policy / eligibility (boolean gates)
|
|
91
|
+
|
|
92
|
+
These **filter** the candidate set. They never enter the ETG sum.
|
|
93
|
+
|
|
94
|
+
| Flag | Meaning | Signal |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `serveable` / `unserveable_reason` | Is the model resolvable & offerable at all (the `UNAVAILABLE` gate; "no delegation") | `v1_routes.py:154-156`; `hugpy_engine/resolvers/model_resolver.py` |
|
|
97
|
+
| `assigned` | **Attribution only** — central designated this box. NOT disk presence, NOT eviction protection. | `assigned_models` (`hugpy_fleet/worker/agent.py:3594`); ruling `hugpy_fleet/worker/agent.py:3263-3272` |
|
|
98
|
+
| `materialized` | Honesty bit: were weights actually **measured** in VRAM (vs merely claimed). `false` ⇒ `RESERVED`, not `LOADED`. | `hugpy_fleet/worker/agent.py:1201-1205` |
|
|
99
|
+
| `pinned` | Never evict from VRAM. | residency/pin config |
|
|
100
|
+
| `protected` (+ `why`) | Transient VRAM-evict shield: `static / loaded / loading / provisioning / store-gate`. | `hugpy_fleet/worker/agent.py:3229-3298` |
|
|
101
|
+
| `blocked` | Operator block — refused from the serving pool. | `hugpy_fleet/central/blocklist.py` |
|
|
102
|
+
| `serves_locally` | Worker **policy**: does this box serve at all (`HUGPY_NO_LOCAL_SERVING`). Unrelated to any model's state. | `remote.py:2449` |
|
|
103
|
+
| `evictable` | Derived: `resident && !pinned && !protected && !mid_generation`. | eviction planner |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Canonical term dictionary (quick lookup)
|
|
108
|
+
|
|
109
|
+
| Term | The one meaning | Layer |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| **unavailable** | not resolvable anywhere; cannot be delegated | 1 (floor) |
|
|
112
|
+
| **cold** | on source-of-truth/origin, **not** on this worker's hot drive | 1 |
|
|
113
|
+
| **hot** | on this worker's hot drive (box-local NVMe), not in VRAM | 1 |
|
|
114
|
+
| **loaded** | measured-resident in VRAM, idle | 1 |
|
|
115
|
+
| **serving** | resident in VRAM and actively answering | 1 |
|
|
116
|
+
| **pull / fetch** | download source-of-truth → hot drive | 1 (transition) |
|
|
117
|
+
| **load** | hot drive → VRAM | 1 (transition) |
|
|
118
|
+
| **evict** | drop from VRAM (→ hot) | 1 (transition) |
|
|
119
|
+
| **reap** | delete from hot drive (→ cold), hot-cache FIFO | 1 (transition) |
|
|
120
|
+
| **hot drive** | the box-local NVMe a worker loads from (`HUGPY_HOT_CACHE_ROOT`) | storage tier |
|
|
121
|
+
| **source of truth** | the shared model array (`MODELS_HOME`), never reaped | storage tier |
|
|
122
|
+
| **t_pull / t_load / t_evict / t_queue / t_generate** | ETG cost terms (seconds) | 2 |
|
|
123
|
+
| **ETG** | estimated time to generation = sum of `t_*` | 2 |
|
|
124
|
+
| **materialized** | measured-in-VRAM honesty bit | 3 |
|
|
125
|
+
| **assigned** | central attribution only | 3 |
|
|
126
|
+
| **serves_locally** | worker serving policy | 3 |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Divergence Register (shore up here; do not repeat)
|
|
131
|
+
|
|
132
|
+
Every known place code contradicts this doc. Resolution = what the code should become.
|
|
133
|
+
|
|
134
|
+
1. **`cold` = "not in VRAM" (should be "not on hot drive").** ✅ RESOLVED (2026-08-29)
|
|
135
|
+
`hugpy_engine/resolvers/remote.py` (`_admit_cold_hold`, cold-hold gate) treated any
|
|
136
|
+
not-`healthy` model as cold, refusing it under the **download** cap even when it was
|
|
137
|
+
already `HOT` (on the worker's drive). The load-state contract had no on-disk field.
|
|
138
|
+
**Fix applied:** `load_state_for_model` (`hugpy_fleet/central/workers.py`) now emits
|
|
139
|
+
`on_disk` from `models_local` (disk-truth), and `_admit_cold_hold` admits a HOT model
|
|
140
|
+
UNCOUNTED — a `HOT` model's admission is a `t_load`, never a `t_pull`, so it no longer
|
|
141
|
+
consumes a download permit. Landed 2026-08-29; patch archived (fully applied, verified
|
|
142
|
+
2026-09-14) to `archive/deploy-patches-20260914/div1-cold-hold-on_disk.patch`.
|
|
143
|
+
|
|
144
|
+
2. **Console labels invert `cold`.** ✅ RESOLVED-by-verification (2026-08-30)
|
|
145
|
+
The LIVE console SPA is already canon-correct: `WorkerRow.jsx deriveModelState()` maps
|
|
146
|
+
`onWorkerDisk?'hot':centralHas?'cold':'missing'` with a `🌡 hot` pill and a correct
|
|
147
|
+
sort-rank ladder. The inversion survives ONLY in a dead backup monolith
|
|
148
|
+
(`backs/WorkersPanel/WorkersPanel.jsx`, not imported/bundled). The backend no longer
|
|
149
|
+
encodes the triple (it ships raw signals; the stale `dispatch.py:86` ref is gone). No
|
|
150
|
+
change needed in live code.
|
|
151
|
+
|
|
152
|
+
3. **`hot`/`cold` = VRAM-at-pick latency bucket.** ✅ RESOLVED (2026-08-30)
|
|
153
|
+
`TEMPERATURES=("hot","cold")` used `hot` for in-VRAM-at-pick. **Fix applied:** buckets
|
|
154
|
+
are now `("loaded","unloaded")` keyed on LOADED-at-pick; `estimate_total_time` default
|
|
155
|
+
and the record path updated; an idempotent SQLite migration rebuckets legacy hot→loaded
|
|
156
|
+
/ cold→unloaded so history is preserved. Landed 2026-08-30; patch archived (fully
|
|
157
|
+
applied, verified 2026-09-14) to `archive/deploy-patches-20260914/div3-6-loaded_at_pick.patch`.
|
|
158
|
+
|
|
159
|
+
4. **Video picker `· cold` = "not answered in 180s".** ✅ RESOLVED (2026-08-30)
|
|
160
|
+
**Fix applied:** `video_prompt_assist_models` now computes the hottest per-key state
|
|
161
|
+
across online workers (serving>loaded>hot>cold, folding in `models_local`+`allocations`)
|
|
162
|
+
and emits a canonical `state` field; the picker (`PromptAssistButtons.tsx`,
|
|
163
|
+
`GenerateStation.tsx`) renders serving/loaded=ready, `· hot (loads)`, `· cold (downloads)`
|
|
164
|
+
— `cold` reserved for not-on-hot-drive. Landed 2026-08-30; patch archived (fully applied
|
|
165
|
+
backend+frontend, verified 2026-09-14) to
|
|
166
|
+
`archive/deploy-patches-20260914/div4-video-picker-canonical-state.patch`.
|
|
167
|
+
|
|
168
|
+
5. **`serving` is three things.** ✅ RESOLVED-by-verification (2026-08-30)
|
|
169
|
+
Audit shows the three concepts are ALREADY distinct at the field level: the alloc-row
|
|
170
|
+
`serving` is computed solely from *answering within `_SERVING_WINDOW_S` (180s)*
|
|
171
|
+
(`_slot_serving` / `last_used<window`, `hugpy_fleet/worker/agent.py`) = canonical SERVING; readiness is the
|
|
172
|
+
separate `healthy` field (load-state seam); policy is `serves_locally` /
|
|
173
|
+
`HUGPY_NO_LOCAL_SERVING`. No rename needed. Residual `"serving"` appeared only as (a) loose
|
|
174
|
+
log prose and (b) a LEGACY INPUT alias for the on-demand residency default (originally
|
|
175
|
+
retained for backward-compat, like `warm` in #6). **Update 2026-08-30 (same day, later):**
|
|
176
|
+
the compat-shim cleanup removed the `"serving"`/`"warm"` legacy input aliases entirely
|
|
177
|
+
(operator ruling: no back-compat) — neither string is accepted by `hugpy_fleet/worker/agent.py`'s residency
|
|
178
|
+
handler or `worker_routes.py:_normalize_residency` any more (verified 2026-09-14). Patch
|
|
179
|
+
archived to `archive/deploy-patches-20260914/div-cleanup-strip-compat-shims.patch`. New
|
|
180
|
+
code must not use "serving" to mean "ready".
|
|
181
|
+
|
|
182
|
+
6. **`warm` is overloaded.** ✅ RESOLVED (2026-08-30)
|
|
183
|
+
**Fix applied:** the `warm_at_pick` metric → `loaded_at_pick` (producer + both readers;
|
|
184
|
+
the telemetry `model.warm` field → `model.loaded_at_pick`). The `"warm"` *input* alias
|
|
185
|
+
(originally retained to map an old client string to the on-demand default) was itself
|
|
186
|
+
removed by the later same-day compat-shim cleanup (operator ruling: no back-compat) —
|
|
187
|
+
`"warm"` no longer appears anywhere in `hugpy_fleet/worker/agent.py` (verified 2026-09-14; cleanup patch
|
|
188
|
+
archived to `archive/deploy-patches-20260914/div-cleanup-strip-compat-shims.patch`).
|
|
189
|
+
Landed 2026-08-30; patch archived to
|
|
190
|
+
`archive/deploy-patches-20260914/div3-6-loaded_at_pick.patch`.
|
|
191
|
+
|
|
192
|
+
7. **`resident` = VRAM in two places, disk in a third.** ✅ RESOLVED (2026-08-30)
|
|
193
|
+
VRAM predicate (canonical, unchanged), eviction unit, and disk `resident_bytes`.
|
|
194
|
+
**#7A:** eviction unit class `Resident` → `EvictUnit` (4 constructors + hints; compat
|
|
195
|
+
alias `Resident = EvictUnit` originally kept). The alias was itself removed by the later
|
|
196
|
+
same-day compat-shim cleanup (no back-compat) — `Resident` no longer appears anywhere in
|
|
197
|
+
`hugpy_engine/eviction.py` (verified 2026-09-14). **#7B:** on-disk footprint
|
|
198
|
+
`resident_bytes`/`resident_model_bytes`/`resident_source` → `hot_bytes`/`hot_model_bytes`/
|
|
199
|
+
`hot_source`, `gauge_basis:"resident"`→`"hot"` (workers.py, worker_routes.py) — this was a
|
|
200
|
+
WIRE key read by the console SPA (`FleetResidency.jsx`, `WorkerStorageBar.jsx`,
|
|
201
|
+
`ResourceStrip.jsx`, `WorkerRow.jsx`); console consumer `WorkerStorageBar.jsx` reads
|
|
202
|
+
`hot_bytes`. No back-compat. Both landed 2026-08-30; patches archived to
|
|
203
|
+
`archive/deploy-patches-20260914/div7a-Resident-to-EvictUnit.patch`,
|
|
204
|
+
`archive/deploy-patches-20260914/div7b-resident_bytes-to-hot_bytes.patch`, and
|
|
205
|
+
(the alias-removal) `archive/deploy-patches-20260914/div-cleanup-strip-compat-shims.patch`.
|
|
206
|
+
|
|
207
|
+
8. **`loaded_models` has three scopes under one name.** ✅ RESOLVED-by-verification (2026-08-30)
|
|
208
|
+
The authoritative shape ALREADY EXISTS: `_allocations()` (heartbeat allocation rows,
|
|
209
|
+
`hugpy_fleet/worker/agent.py`) reports one row per slot-seated model ∪ per in-process resident — explicitly
|
|
210
|
+
"a NEW field parallel to loaded_models/slots so old central/UI keep working."
|
|
211
|
+
`loaded_model_keys()` deliberately subtracts `slot_backed_model_keys()` (stops the
|
|
212
|
+
loaded/serving flap). `loaded_models` is INTENTIONALLY retained as backward-compat; the
|
|
213
|
+
canonical residency truth is `allocations`. No change made — the operator's compat design
|
|
214
|
+
is deliberate.
|
|
215
|
+
|
|
216
|
+
9. **`/health` is not residency-truthful.** ✅ RESOLVED-by-verification (2026-08-30)
|
|
217
|
+
Verified the DECISION path already conforms: the central residency predicate
|
|
218
|
+
`_resident_on()` reads the heartbeat `allocations` rows (honoring a slot row only when
|
|
219
|
+
`healthy`/`serving`/`busy`) plus `loaded_models` — never `/health`. The only `/health`
|
|
220
|
+
residency read is an ADDITIVE overlay in `load_state_for_model` that can turn "no
|
|
221
|
+
movement" into movement but never the reverse, so it cannot produce a false
|
|
222
|
+
"nothing loaded". No decision is misled; no change needed.
|
|
223
|
+
|
|
224
|
+
10. **`in_flight` overloaded.** ✅ RESOLVED (2026-08-30)
|
|
225
|
+
Request-concurrency cap vs eviction mid-generation lock shared the word. **Fix
|
|
226
|
+
applied:** the eviction lock `Resident.in_flight` → `mid_generation` (internal, the
|
|
227
|
+
misleading one — removes the collision); the central `WorkerBusyError` concurrency
|
|
228
|
+
field → `requests_in_flight` (the `"in_flight"` error-payload wire key originally kept
|
|
229
|
+
for client compat). That wire-key alias was itself dropped by the later same-day
|
|
230
|
+
compat-shim cleanup (no back-compat) — `remote.py`'s payload now emits only
|
|
231
|
+
`requests_in_flight` (verified 2026-09-14). gen_gate concurrency counters keep
|
|
232
|
+
`in_flight` (unambiguous — different subsystem). Landed 2026-08-30; patch archived to
|
|
233
|
+
`archive/deploy-patches-20260914/div10-in_flight-disambiguation.patch`.
|
|
234
|
+
|
|
235
|
+
11. **`in_progress` fuses pull + load.** ✅ RESOLVED (2026-08-30)
|
|
236
|
+
Load-state field documented as "weights loading OR still downloading now" only fed
|
|
237
|
+
error wording. **Fix applied:** `load_state_for_model` now emits `pulling` (COLD→HOT
|
|
238
|
+
provisioning) and `loading` (HOT→VRAM) as distinct flags, keeping `in_progress` as
|
|
239
|
+
their union for forward-progress callers; the `cold_load_capacity` refusal now names
|
|
240
|
+
the actual transition ("still downloading onto" / "still loading into VRAM on" / "not
|
|
241
|
+
loaded yet"). Landed 2026-08-30; patch archived to
|
|
242
|
+
`archive/deploy-patches-20260914/div11-in_progress-split.patch`.
|
|
243
|
+
|
|
244
|
+
12. **`assigned` was conflated with disk presence.** ✅ RESOLVED (by prior ruling) — stated here:
|
|
245
|
+
`assigned_models` is attribution only (`hugpy_fleet/worker/agent.py:3263-3272`, `hugpy_fleet/central/workers.py:2245-2253`);
|
|
246
|
+
the on-disk signal is `models_local` (`hugpy_fleet/worker/agent.py:5129`). Do not reintroduce the conflation.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Retired words (do not use in new code)
|
|
251
|
+
|
|
252
|
+
| Retired | Use instead |
|
|
253
|
+
|---|---|
|
|
254
|
+
| `warm` (any sense) | `LOADED` / `SERVING` (state) or `residency_mode="on_demand"` (config) |
|
|
255
|
+
| `hot` for "in VRAM" | `LOADED` / `SERVING`; `hot` means **on the hot drive** only |
|
|
256
|
+
| `cold` for "not in VRAM" | `HOT` (on drive, not loaded) — `cold` means **not on the hot drive** |
|
|
257
|
+
| `serving` for "ready/healthy" | `LOADED` (resident-idle) |
|
|
258
|
+
| `serving` for the policy | `serves_locally` |
|
|
259
|
+
| `resident` for on-disk bytes | `hot_bytes` |
|
|
260
|
+
| `in_flight` for evict lock | `mid_generation` |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Glossary anchors (source of truth for the reconciliation)
|
|
265
|
+
|
|
266
|
+
- Hot-drive doctrine (verbatim operator ruling): `hugpy_engine/serve/hot_cache.py` header.
|
|
267
|
+
- Two-stage lifecycle (`provision.*` vs `load.*`) and residency tiers: `hugpy_fleet/central/evictions.py:110-183`.
|
|
268
|
+
- ETG estimator (orphaned): `hugpy_fleet/central/model_metrics.py:8-9,265-292`.
|
|
269
|
+
- Disk-truth signal: `hugpy_fleet/worker/agent.py:5129-5171` (`models_local`).
|
|
270
|
+
- Residency truth (heartbeat allocation rows): `hugpy_fleet/worker/agent.py:1830-2010`.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Per-worker BOOT-LOAD STAR (`boot_prewarm`)
|
|
2
|
+
|
|
3
|
+
Operator RULING **2026-07-23 (post-incident)** — verbatim:
|
|
4
|
+
|
|
5
|
+
- "the star is only supposed to indicate **load that model on boot**."
|
|
6
|
+
- "it **shouldn't effect anything but priority for ambiguous model calls**."
|
|
7
|
+
|
|
8
|
+
A worker carries a single ⭐ **star** model. The star does **exactly two things
|
|
9
|
+
and nothing else**:
|
|
10
|
+
|
|
11
|
+
1. **Load on boot** — once per worker process lifetime.
|
|
12
|
+
2. **Ranking priority** — a tie-break for ambiguous / no-warm model calls:
|
|
13
|
+
central prefers the worker whose star == the requested model.
|
|
14
|
+
|
|
15
|
+
It is **NOT keep-warm**, it does **NOT re-warm after eviction**, and it has **no
|
|
16
|
+
eviction interaction**. A starred model that gets evicted under pressure **stays
|
|
17
|
+
cold until the worker process restarts**. The identifier stays
|
|
18
|
+
`boot_prewarm`/`boot-prewarm` (rename churn isn't worth it) and once again means
|
|
19
|
+
exactly **boot-once**.
|
|
20
|
+
|
|
21
|
+
> **Why the revert (incident 2026-07-23).** The prior release (0.1.201) made the
|
|
22
|
+
> star **reconcile-kept-warm** — central re-warmed it every beat and the worker
|
|
23
|
+
> reloaded it whenever it was absent. On **ae** that re-warm fired against
|
|
24
|
+
> **coder-next while active inference was in flight** → the star's slot child
|
|
25
|
+
> stalled → a zombie seat → the agent froze. Re-warm-after-eviction is only safe
|
|
26
|
+
> once a **co-fit gate** (reload an evicted model only when it co-fits its
|
|
27
|
+
> evictor) exists — that is **future work (Slice D), not yet built** — so the
|
|
28
|
+
> star is strictly boot-once until then.
|
|
29
|
+
|
|
30
|
+
## The three levers (locked semantics)
|
|
31
|
+
|
|
32
|
+
| lever | keeps warm? | boot-loads? | eviction | notes |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| ⭐ **star** (`boot_prewarm`) | **No** | **Yes — once, on boot** | **Evictable; once evicted STAYS cold until restart** | Also a **ranking tie-break** for ambiguous calls. NOT eviction-protected, NOT re-warmed. |
|
|
35
|
+
| 🔒 **static** | **Yes** — the keep-warm tier | yes (eager) | **Protected** — never evicted by the LLM plane | "start here AND stay here". |
|
|
36
|
+
| 📌 **pin** | **No** | no | n/a | Routing persistence only — never warms. |
|
|
37
|
+
|
|
38
|
+
🔒 **static is the keep-warm tier** — the one lever that keeps a model resident
|
|
39
|
+
across evictions. If you want "start here **and stay here**", promote the model
|
|
40
|
+
to static; the ⭐ star will not hold it.
|
|
41
|
+
|
|
42
|
+
And for contrast, the /media star:
|
|
43
|
+
|
|
44
|
+
| | media_default (the /media star) | ⭐ boot_prewarm (this per-worker star) |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Scope | one global model | one model **per worker** |
|
|
47
|
+
| Effect | "first in the list + default-selected" — a **UI/routing preference** | "**boot-load** this model + rank it first for ambiguous calls" |
|
|
48
|
+
| Loads anything? | **No** | **Yes — once, on the worker's boot** |
|
|
49
|
+
|
|
50
|
+
**Nothing warms until starred (boot-load) or static.** The fleet `TASK_DEFAULTS`
|
|
51
|
+
(sd-turbo et al.) are a **routing fallback** — a request naming only a task still
|
|
52
|
+
resolves to its default model (`model_resolver.TASK_DEFAULTS`) — but they are
|
|
53
|
+
**not kept warm** (the task-defaults floor is dead and is not coming back). 📌
|
|
54
|
+
pins never warm. Everything but 🔒static lazy-loads on first real request (the
|
|
55
|
+
star additionally boot-loads once).
|
|
56
|
+
|
|
57
|
+
### How the star works
|
|
58
|
+
|
|
59
|
+
- **Worker side — boot-load once.** `_adopt_boot_prewarm` runs on every
|
|
60
|
+
register/heartbeat reply, but a **process-lifetime done-latch**
|
|
61
|
+
(`_BOOT_PREWARM_DONE`) makes it fire the load **exactly once**: on the **first**
|
|
62
|
+
reply carrying a star. It loads through the normal on-demand path (no residency
|
|
63
|
+
write — the model stays FIFO-evictable). Every later beat is a **no-op**,
|
|
64
|
+
**including after an eviction** — the star is **not** reloaded. A genuine retry
|
|
65
|
+
only comes with a worker **restart**. Missing model → logged, never crashes.
|
|
66
|
+
- **Central side — ranking priority only.** `_reconcile_warm_set(worker)` keeps
|
|
67
|
+
warm **`🔒static ∩ models_local − blocked`** — the **star is not in it**, so
|
|
68
|
+
central never re-probes the star warm. The star's only central effect is a
|
|
69
|
+
**ranking tie-break**: in `pick_for_model` / `candidates_for_model` the sort key
|
|
70
|
+
is `(home, warm, star, gpu, last_picked, id)` — home beats everything, a warm
|
|
71
|
+
box beats a starred box, and the star breaks the tie when **nothing is warm**
|
|
72
|
+
(prefer the box that boot-loads the model anyway). Alias-tolerant: a star
|
|
73
|
+
recorded under a `~`-qualified key matches a bare-key request and vice versa.
|
|
74
|
+
|
|
75
|
+
**Future work (Slice D, not built): co-fit-gated re-entry** — an evicted star
|
|
76
|
+
would reload only when it **co-fits** with whatever evicted it (no thrash by
|
|
77
|
+
construction). Until that exists, an evicted star stays cold until restart.
|
|
78
|
+
|
|
79
|
+
## API
|
|
80
|
+
|
|
81
|
+
All paths are relative to central (e.g. `https://dev.hugpy.ai/api`). Worker ids
|
|
82
|
+
come from `GET /llm/workers` (the `id` field).
|
|
83
|
+
|
|
84
|
+
- **Set / replace a worker's star** (operator-gated):
|
|
85
|
+
`POST /llm/workers/<worker_id>/boot-prewarm`
|
|
86
|
+
Body: `{"model_key": "<key>", "enabled": true}` — makes `model_key` the star,
|
|
87
|
+
replacing any previous one. `{"enabled": false}` (optionally with a matching
|
|
88
|
+
`model_key`, or none) clears it.
|
|
89
|
+
|
|
90
|
+
- **Read the full star map** (open, read tier): `GET /llm/workers/boot-prewarm`
|
|
91
|
+
→ `{"<worker_id>": "<model_key>", ...}`
|
|
92
|
+
|
|
93
|
+
- **Per-worker surfacing**: every row of `GET /llm/workers` (and
|
|
94
|
+
`GET /llm/workers/<id>`) carries `"boot_prewarm": "<model_key>"|null`, so the
|
|
95
|
+
console can render the star.
|
|
96
|
+
|
|
97
|
+
The star rides the register/heartbeat reply to the worker as
|
|
98
|
+
`boot_prewarm: "<model_key>"` — additive and **omit-when-unset**, so a released
|
|
99
|
+
worker that predates the feature simply ignores it (the `extra=forbid` relay
|
|
100
|
+
schema is never broken).
|
|
101
|
+
|
|
102
|
+
## Seed the operator's two defaults
|
|
103
|
+
|
|
104
|
+
These are **data, not code** — run them once against central with the operator
|
|
105
|
+
token. Replace `<computron-id>` / `<ae-id>` with the ids from `GET /llm/workers`.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# computron (RTX-4060) -> a present 7B. Use whichever 7B key the fleet actually
|
|
109
|
+
# holds (check `GET /llm/workers/<computron-id>` models / `GET /models`):
|
|
110
|
+
# Qwen~Qwen2-7B-Instruct-GGUF (or Qwen~Qwen2.5-7B-Instruct-GGUF)
|
|
111
|
+
curl -fsS -X POST https://dev.hugpy.ai/api/llm/workers/<computron-id>/boot-prewarm \
|
|
112
|
+
-H "X-Operator-Token: $HUGPY_OPERATOR_TOKEN" \
|
|
113
|
+
-H "Content-Type: application/json" \
|
|
114
|
+
-d '{"model_key": "Qwen~Qwen2-7B-Instruct-GGUF", "enabled": true}'
|
|
115
|
+
|
|
116
|
+
# ae (RTX-3090) -> the agent brain, coder-next (DEFAULT_AGENT_BRAIN):
|
|
117
|
+
curl -fsS -X POST https://dev.hugpy.ai/api/llm/workers/<ae-id>/boot-prewarm \
|
|
118
|
+
-H "X-Operator-Token: $HUGPY_OPERATOR_TOKEN" \
|
|
119
|
+
-H "Content-Type: application/json" \
|
|
120
|
+
-d '{"model_key": "Qwen~Qwen3-Coder-Next-GGUF", "enabled": true}'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Verify: `GET /llm/workers/boot-prewarm` returns both, and each worker
|
|
124
|
+
**boot-loads** its star once on start (`boot star … loading once at boot …` in
|
|
125
|
+
the agent log; then every later beat is a no-op — the star is **not** re-warmed).
|
|
126
|
+
An evicted star stays cold until the worker restarts.
|
|
127
|
+
|
|
128
|
+
### Note on the 7B key for computron
|
|
129
|
+
|
|
130
|
+
The brief named `Qwen2-7B-Instruct-GGUF` / `Qwen2.5 7B`. Neither is a curated
|
|
131
|
+
staple in `models_config.MODELS` (they arrive via discovery), so the exact
|
|
132
|
+
`model_key` is whatever the fleet discovered — confirm against `GET /models`
|
|
133
|
+
before seeding. The star store does **not** require the model to be present or
|
|
134
|
+
allocated when you set it; if the model can't be loaded, the worker logs and
|
|
135
|
+
continues (never crashes).
|