hugpy-engine 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.
Files changed (185) hide show
  1. hugpy_engine-0.2.2/LICENSE +41 -0
  2. hugpy_engine-0.2.2/PKG-INFO +116 -0
  3. hugpy_engine-0.2.2/README.md +68 -0
  4. hugpy_engine-0.2.2/docs/ALLOCATION-MODES.md +174 -0
  5. hugpy_engine-0.2.2/pyproject.toml +82 -0
  6. hugpy_engine-0.2.2/setup.cfg +4 -0
  7. hugpy_engine-0.2.2/src/hugpy_engine/__init__.py +163 -0
  8. hugpy_engine-0.2.2/src/hugpy_engine/alloc_modes.py +1342 -0
  9. hugpy_engine-0.2.2/src/hugpy_engine/allocation.py +68 -0
  10. hugpy_engine-0.2.2/src/hugpy_engine/apis/__init__.py +0 -0
  11. hugpy_engine-0.2.2/src/hugpy_engine/apis/call_api.py +149 -0
  12. hugpy_engine-0.2.2/src/hugpy_engine/apis/get_module.py +601 -0
  13. hugpy_engine-0.2.2/src/hugpy_engine/apis/reclassify.py +238 -0
  14. hugpy_engine-0.2.2/src/hugpy_engine/apis/reconcile.py +628 -0
  15. hugpy_engine-0.2.2/src/hugpy_engine/apis/serve/__init__.py +22 -0
  16. hugpy_engine-0.2.2/src/hugpy_engine/apis/serve/serve.py +33 -0
  17. hugpy_engine-0.2.2/src/hugpy_engine/apis/serve/serve_cli.py +9 -0
  18. hugpy_engine-0.2.2/src/hugpy_engine/apis/systemd_units.py +9 -0
  19. hugpy_engine-0.2.2/src/hugpy_engine/backend.py +86 -0
  20. hugpy_engine-0.2.2/src/hugpy_engine/backends/__init__.py +5 -0
  21. hugpy_engine-0.2.2/src/hugpy_engine/backends/local.py +163 -0
  22. hugpy_engine-0.2.2/src/hugpy_engine/catalog.py +99 -0
  23. hugpy_engine-0.2.2/src/hugpy_engine/catalog_bridge.py +223 -0
  24. hugpy_engine-0.2.2/src/hugpy_engine/categories.py +132 -0
  25. hugpy_engine-0.2.2/src/hugpy_engine/chat_context/__init__.py +27 -0
  26. hugpy_engine-0.2.2/src/hugpy_engine/chat_context/chat_context.py +223 -0
  27. hugpy_engine-0.2.2/src/hugpy_engine/chat_context/context_budget.py +223 -0
  28. hugpy_engine-0.2.2/src/hugpy_engine/chat_context/unbounded.py +55 -0
  29. hugpy_engine-0.2.2/src/hugpy_engine/config/__init__.py +0 -0
  30. hugpy_engine-0.2.2/src/hugpy_engine/config/main.py +270 -0
  31. hugpy_engine-0.2.2/src/hugpy_engine/config/models/__init__.py +0 -0
  32. hugpy_engine-0.2.2/src/hugpy_engine/config/models/model_meta.py +198 -0
  33. hugpy_engine-0.2.2/src/hugpy_engine/config/models/models_config.py +1629 -0
  34. hugpy_engine-0.2.2/src/hugpy_engine/config/models/models_default.py +196 -0
  35. hugpy_engine-0.2.2/src/hugpy_engine/config/models/models_dict.py +73 -0
  36. hugpy_engine-0.2.2/src/hugpy_engine/contracts.py +44 -0
  37. hugpy_engine-0.2.2/src/hugpy_engine/dispatch/__init__.py +37 -0
  38. hugpy_engine-0.2.2/src/hugpy_engine/dispatch/acquire.py +59 -0
  39. hugpy_engine-0.2.2/src/hugpy_engine/dispatch/activity.py +110 -0
  40. hugpy_engine-0.2.2/src/hugpy_engine/dispatch/dispatch.py +1151 -0
  41. hugpy_engine-0.2.2/src/hugpy_engine/draft_models.py +72 -0
  42. hugpy_engine-0.2.2/src/hugpy_engine/engines.py +44 -0
  43. hugpy_engine-0.2.2/src/hugpy_engine/eviction.py +662 -0
  44. hugpy_engine-0.2.2/src/hugpy_engine/generate/__init__.py +16 -0
  45. hugpy_engine-0.2.2/src/hugpy_engine/generate/coder.py +667 -0
  46. hugpy_engine-0.2.2/src/hugpy_engine/generate/config.py +187 -0
  47. hugpy_engine-0.2.2/src/hugpy_engine/generate/generate_runner.py +273 -0
  48. hugpy_engine-0.2.2/src/hugpy_engine/gguf_election.py +259 -0
  49. hugpy_engine-0.2.2/src/hugpy_engine/llama/__init__.py +23 -0
  50. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/__init__.py +23 -0
  51. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/chat_runner.py +245 -0
  52. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/get.py +772 -0
  53. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/__init__.py +9 -0
  54. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/base_runner.py +609 -0
  55. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/ccp_runner.py +332 -0
  56. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/imports/__init__.py +25 -0
  57. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/imports/config.py +16 -0
  58. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/imports/constants.py +38 -0
  59. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/imports/init_imports.py +6 -0
  60. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/imports/utils.py +64 -0
  61. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/python_runner.py +476 -0
  62. hugpy_engine-0.2.2/src/hugpy_engine/llama/runners/src/shard_server.py +341 -0
  63. hugpy_engine-0.2.2/src/hugpy_engine/llama/serve.py +9 -0
  64. hugpy_engine-0.2.2/src/hugpy_engine/manifest.py +53 -0
  65. hugpy_engine-0.2.2/src/hugpy_engine/model_classifier.py +382 -0
  66. hugpy_engine-0.2.2/src/hugpy_engine/model_index/__init__.py +82 -0
  67. hugpy_engine-0.2.2/src/hugpy_engine/model_index/client.py +101 -0
  68. hugpy_engine-0.2.2/src/hugpy_engine/model_index/query_registry.py +399 -0
  69. hugpy_engine-0.2.2/src/hugpy_engine/model_index/repositories.py +260 -0
  70. hugpy_engine-0.2.2/src/hugpy_engine/model_index/service.py +281 -0
  71. hugpy_engine-0.2.2/src/hugpy_engine/name_match.py +287 -0
  72. hugpy_engine-0.2.2/src/hugpy_engine/native/__init__.py +19 -0
  73. hugpy_engine-0.2.2/src/hugpy_engine/native/build.py +98 -0
  74. hugpy_engine-0.2.2/src/hugpy_engine/native/fetch.py +194 -0
  75. hugpy_engine-0.2.2/src/hugpy_engine/native/resolve.py +352 -0
  76. hugpy_engine-0.2.2/src/hugpy_engine/output_repair.py +212 -0
  77. hugpy_engine-0.2.2/src/hugpy_engine/peft_adapters.py +286 -0
  78. hugpy_engine-0.2.2/src/hugpy_engine/placement.py +457 -0
  79. hugpy_engine-0.2.2/src/hugpy_engine/py.typed +0 -0
  80. hugpy_engine-0.2.2/src/hugpy_engine/query.py +191 -0
  81. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/__init__.py +25 -0
  82. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/allocator.py +126 -0
  83. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/assure_model_key.py +430 -0
  84. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/categories/__init__.py +22 -0
  85. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/categories/builders.py +163 -0
  86. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/categories/frameworks.py +67 -0
  87. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/device_placement.py +212 -0
  88. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/groups.py +651 -0
  89. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/model_dict_resolver.py +23 -0
  90. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/model_resolver.py +628 -0
  91. hugpy_engine-0.2.2/src/hugpy_engine/resolvers/remote.py +3873 -0
  92. hugpy_engine-0.2.2/src/hugpy_engine/routing_diagnostics.py +265 -0
  93. hugpy_engine-0.2.2/src/hugpy_engine/runtime.py +84 -0
  94. hugpy_engine-0.2.2/src/hugpy_engine/schemas/__init__.py +0 -0
  95. hugpy_engine-0.2.2/src/hugpy_engine/schemas/chat_schemas.py +336 -0
  96. hugpy_engine-0.2.2/src/hugpy_engine/schemas/event_schemas.py +57 -0
  97. hugpy_engine-0.2.2/src/hugpy_engine/schemas/metadata_schemas.py +62 -0
  98. hugpy_engine-0.2.2/src/hugpy_engine/schemas/model_schemas.py +101 -0
  99. hugpy_engine-0.2.2/src/hugpy_engine/schemas/runner_schemas.py +33 -0
  100. hugpy_engine-0.2.2/src/hugpy_engine/schemas/task_schemas.py +45 -0
  101. hugpy_engine-0.2.2/src/hugpy_engine/serve/__init__.py +57 -0
  102. hugpy_engine-0.2.2/src/hugpy_engine/serve/chunksum_verify.py +213 -0
  103. hugpy_engine-0.2.2/src/hugpy_engine/serve/hot_cache.py +1014 -0
  104. hugpy_engine-0.2.2/src/hugpy_engine/serve/load_failure.py +188 -0
  105. hugpy_engine-0.2.2/src/hugpy_engine/serve/model_cache.py +218 -0
  106. hugpy_engine-0.2.2/src/hugpy_engine/serve/overrides.py +1075 -0
  107. hugpy_engine-0.2.2/src/hugpy_engine/serve/policy.py +45 -0
  108. hugpy_engine-0.2.2/src/hugpy_engine/serve/profiles.py +364 -0
  109. hugpy_engine-0.2.2/src/hugpy_engine/serve/serve.py +930 -0
  110. hugpy_engine-0.2.2/src/hugpy_engine/serve/serve_cli.py +79 -0
  111. hugpy_engine-0.2.2/src/hugpy_engine/serve/slot_agent.py +2444 -0
  112. hugpy_engine-0.2.2/src/hugpy_engine/serve/slots.py +799 -0
  113. hugpy_engine-0.2.2/src/hugpy_engine/serve/supervisor.py +148 -0
  114. hugpy_engine-0.2.2/src/hugpy_engine/spill.py +1586 -0
  115. hugpy_engine-0.2.2/src/hugpy_engine/task_deps.py +67 -0
  116. hugpy_engine-0.2.2/src/hugpy_engine/tasks.py +349 -0
  117. hugpy_engine-0.2.2/src/hugpy_engine/utils/__init__.py +0 -0
  118. hugpy_engine-0.2.2/src/hugpy_engine/utils/json_scavenge.py +111 -0
  119. hugpy_engine-0.2.2/src/hugpy_engine/utils/no_think.py +325 -0
  120. hugpy_engine-0.2.2/src/hugpy_engine/wire/__init__.py +0 -0
  121. hugpy_engine-0.2.2/src/hugpy_engine/wire/chat_schemas.py +66 -0
  122. hugpy_engine-0.2.2/src/hugpy_engine/wire/config_schemas.py +31 -0
  123. hugpy_engine-0.2.2/src/hugpy_engine/wire/install_schemas.py +17 -0
  124. hugpy_engine-0.2.2/src/hugpy_engine/wire/model_schemas.py +43 -0
  125. hugpy_engine-0.2.2/src/hugpy_engine/wire/request_schemas.py +13 -0
  126. hugpy_engine-0.2.2/src/hugpy_engine/wire/specs_schemas.py +9 -0
  127. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/PKG-INFO +116 -0
  128. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/SOURCES.txt +183 -0
  129. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/dependency_links.txt +1 -0
  130. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/entry_points.txt +2 -0
  131. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/requires.txt +28 -0
  132. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/scm_file_list.json +179 -0
  133. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/scm_version.json +8 -0
  134. hugpy_engine-0.2.2/src/hugpy_engine.egg-info/top_level.txt +1 -0
  135. hugpy_engine-0.2.2/tests/conftest.py +23 -0
  136. hugpy_engine-0.2.2/tests/test_admission_gate_and_diagnostics.py +178 -0
  137. hugpy_engine-0.2.2/tests/test_allocation_accuracy.py +257 -0
  138. hugpy_engine-0.2.2/tests/test_budget_bar_spec.py +179 -0
  139. hugpy_engine-0.2.2/tests/test_catalog_bridge.py +100 -0
  140. hugpy_engine-0.2.2/tests/test_central_cap_gating.py +264 -0
  141. hugpy_engine-0.2.2/tests/test_chat_request_caller.py +40 -0
  142. hugpy_engine-0.2.2/tests/test_chat_role_alternation.py +535 -0
  143. hugpy_engine-0.2.2/tests/test_chat_tool_messages_schema.py +113 -0
  144. hugpy_engine-0.2.2/tests/test_chunksum_verify.py +231 -0
  145. hugpy_engine-0.2.2/tests/test_comms_foundations.py +113 -0
  146. hugpy_engine-0.2.2/tests/test_ctx_fit.py +288 -0
  147. hugpy_engine-0.2.2/tests/test_device_placement.py +151 -0
  148. hugpy_engine-0.2.2/tests/test_engine_boundary.py +98 -0
  149. hugpy_engine-0.2.2/tests/test_engine_persisted_resolve.py +295 -0
  150. hugpy_engine-0.2.2/tests/test_eviction_flow.py +411 -0
  151. hugpy_engine-0.2.2/tests/test_feasible_distribution_engine.py +89 -0
  152. hugpy_engine-0.2.2/tests/test_gguf_election.py +301 -0
  153. hugpy_engine-0.2.2/tests/test_gguf_sharded_effective.py +162 -0
  154. hugpy_engine-0.2.2/tests/test_hot_cache.py +330 -0
  155. hugpy_engine-0.2.2/tests/test_image_task_classification.py +405 -0
  156. hugpy_engine-0.2.2/tests/test_import_policy.py +107 -0
  157. hugpy_engine-0.2.2/tests/test_load_failure_reason.py +188 -0
  158. hugpy_engine-0.2.2/tests/test_mmproj_effective_bytes.py +79 -0
  159. hugpy_engine-0.2.2/tests/test_model_aliases.py +86 -0
  160. hugpy_engine-0.2.2/tests/test_no_makeroom.py +316 -0
  161. hugpy_engine-0.2.2/tests/test_no_think.py +329 -0
  162. hugpy_engine-0.2.2/tests/test_peft_adapter_load.py +379 -0
  163. hugpy_engine-0.2.2/tests/test_per_device_slot.py +125 -0
  164. hugpy_engine-0.2.2/tests/test_per_request_alloc.py +86 -0
  165. hugpy_engine-0.2.2/tests/test_placement_defaults.py +104 -0
  166. hugpy_engine-0.2.2/tests/test_provision_failure_honesty.py +120 -0
  167. hugpy_engine-0.2.2/tests/test_registry_attribution.py +144 -0
  168. hugpy_engine-0.2.2/tests/test_registry_symmetry.py +172 -0
  169. hugpy_engine-0.2.2/tests/test_resolve_routing_key_reconcile.py +92 -0
  170. hugpy_engine-0.2.2/tests/test_runner_registry_heartbeat_starvation.py +100 -0
  171. hugpy_engine-0.2.2/tests/test_slot_load_stall_health.py +228 -0
  172. hugpy_engine-0.2.2/tests/test_slot_moe_vram_preflight.py +169 -0
  173. hugpy_engine-0.2.2/tests/test_slot_placement_projection.py +37 -0
  174. hugpy_engine-0.2.2/tests/test_slot_port_guard.py +50 -0
  175. hugpy_engine-0.2.2/tests/test_slot_relaunch.py +211 -0
  176. hugpy_engine-0.2.2/tests/test_spill_ctx_reserve.py +406 -0
  177. hugpy_engine-0.2.2/tests/test_spill_vision_fit.py +233 -0
  178. hugpy_engine-0.2.2/tests/test_tasks_registry.py +186 -0
  179. hugpy_engine-0.2.2/tests/test_tok_s_plausible_ceiling.py +51 -0
  180. hugpy_engine-0.2.2/tests/test_transformers_placement.py +130 -0
  181. hugpy_engine-0.2.2/tests/test_video_model_classification.py +289 -0
  182. hugpy_engine-0.2.2/tests/test_vision_needs_slot.py +242 -0
  183. hugpy_engine-0.2.2/tests/test_vram_cliff.py +766 -0
  184. hugpy_engine-0.2.2/tests/test_worker_error_attribution.py +66 -0
  185. hugpy_engine-0.2.2/tests/test_worker_serve_path_central_only.py +104 -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,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: hugpy-engine
3
+ Version: 0.2.2
4
+ Summary: Hugpy engine: model discovery and classification, allocation and eviction, native llama.cpp and GGUF runners, and the prompt-to-reply inference path with task and placement seams
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/inference/hugpy_engine/README.md
9
+ Project-URL: Repository, https://github.com/hugpy/hugpy
10
+ Project-URL: Source, https://github.com/hugpy/hugpy/tree/main/py/inference/hugpy_engine
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,inference,llama.cpp,gguf,transformers,model-serving
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: abstract-essentials
28
+ Requires-Dist: pydantic>=2
29
+ Requires-Dist: httpx
30
+ Requires-Dist: requests
31
+ Requires-Dist: huggingface-hub
32
+ Requires-Dist: psutil
33
+ Provides-Extra: gguf
34
+ Requires-Dist: llama-cpp-python; extra == "gguf"
35
+ Provides-Extra: transformers
36
+ Requires-Dist: torch; extra == "transformers"
37
+ Requires-Dist: transformers; extra == "transformers"
38
+ Requires-Dist: accelerate; extra == "transformers"
39
+ Requires-Dist: safetensors; extra == "transformers"
40
+ Provides-Extra: finetune
41
+ Requires-Dist: peft; extra == "finetune"
42
+ Provides-Extra: index
43
+ Requires-Dist: psycopg[binary]; extra == "index"
44
+ Provides-Extra: test
45
+ Requires-Dist: pytest>=8; extra == "test"
46
+ Requires-Dist: pytest-timeout; extra == "test"
47
+ Dynamic: license-file
48
+
49
+ # hugpy-engine
50
+
51
+ `hugpy-engine` is the standalone boundary for Hugpy's complete inference path:
52
+
53
+ ```text
54
+ prompt/messages
55
+ -> model discovery and resolution
56
+ -> RAM/VRAM allocation and admission
57
+ -> engine and runner selection/loading
58
+ -> generation and continuation
59
+ -> streamed events or a completed reply
60
+ ```
61
+
62
+ The package is split by responsibility:
63
+
64
+ - `catalog` — discovered models, task/media/default model resolution;
65
+ - `allocation` — feasible modes, default placement, spill and admission plans;
66
+ - `engines` — native llama.cpp discovery and GGUF runner lifecycle;
67
+ - `runtime` — request resolution, execution, runner caching and eviction;
68
+ - `query` — prompt/messages to streamed events, text, or `QueryResult`;
69
+ - `backend` — the injection contract joining those layers to an implementation.
70
+
71
+ The package imports CPU-only: `import hugpy_engine` never loads llama_cpp,
72
+ torch, transformers or peft. Those arrive through extras and are imported by
73
+ the runner that needs them:
74
+
75
+ | extra | provides |
76
+ |---|---|
77
+ | `hugpy-engine[gguf]` | in-process GGUF inference via `llama-cpp-python` |
78
+ | `hugpy-engine[transformers]` | the transformers text-generation runner |
79
+ | `hugpy-engine[finetune]` | PEFT adapter loading |
80
+ | `hugpy-engine[index]` | the optional Postgres model index |
81
+
82
+ The default backend is the in-process `LocalBackend` (`hugpy_engine.backends`),
83
+ built from the engine's own catalog, allocation, native-engine and dispatch
84
+ modules; `configure_backend(...)` / `backend_scope(...)` swap in another
85
+ `InferenceBackend` (a fake in tests, a remote proxy in a thin client).
86
+
87
+ ```python
88
+ from hugpy_engine import query_sync
89
+
90
+ reply = query_sync("Explain tensor parallelism", model_key="my-model")
91
+ ```
92
+
93
+ Async callers use `await query(...)`. `stream_query(...)` yields engine events;
94
+ `query_result(...)` retains request ID, finish reason, usage, and timings.
95
+
96
+ ## Seams (what the engine asks of the packages above it)
97
+
98
+ - `hugpy_engine.placement` — Protocols + `get_*()/set_*()` providers for
99
+ everything the engine asks about the fleet (worker registry and key forms,
100
+ worker HTTP transport/breaker, eviction telemetry ledger, blocklist, model
101
+ metrics, priority groups). Null defaults mean "no fleet"; `hugpy_fleet`
102
+ implements them and the server wires them.
103
+ - `hugpy_engine.tasks` — the task-runner registry. Media and video runners
104
+ plug in with `register_task(task, runner=..., build_request=...,
105
+ frameworks=(...), extra=..., source=...)`; `FRAMEWORK_RUNNERS`,
106
+ `MODEL_REQUEST_BUILDERS` and `KNOWN_TASKS_REGISTRY` in
107
+ `hugpy_engine.resolvers.categories` are live views over it. Packages may
108
+ also expose a `hugpy_engine.tasks` entry point (a zero-arg register
109
+ function); the engine loads those on first use.
110
+ - `hugpy_engine.catalog_bridge` — installs the engine's registry as
111
+ `hugpy_storage`'s catalog source, the hot cache as its serve-path hook, and
112
+ refreshes discovery on `hugpy_control.bus` `catalog.changed` events.
113
+ `LocalBackend` installs it lazily.
114
+ - `hugpy_engine.name_match` — the pure eliminate-then-rank name pipeline
115
+ (`resolve_name`, `Candidate`) that `assure_model_key` uses; the oracle
116
+ re-exports it.
@@ -0,0 +1,68 @@
1
+ # hugpy-engine
2
+
3
+ `hugpy-engine` is the standalone boundary for Hugpy's complete inference path:
4
+
5
+ ```text
6
+ prompt/messages
7
+ -> model discovery and resolution
8
+ -> RAM/VRAM allocation and admission
9
+ -> engine and runner selection/loading
10
+ -> generation and continuation
11
+ -> streamed events or a completed reply
12
+ ```
13
+
14
+ The package is split by responsibility:
15
+
16
+ - `catalog` — discovered models, task/media/default model resolution;
17
+ - `allocation` — feasible modes, default placement, spill and admission plans;
18
+ - `engines` — native llama.cpp discovery and GGUF runner lifecycle;
19
+ - `runtime` — request resolution, execution, runner caching and eviction;
20
+ - `query` — prompt/messages to streamed events, text, or `QueryResult`;
21
+ - `backend` — the injection contract joining those layers to an implementation.
22
+
23
+ The package imports CPU-only: `import hugpy_engine` never loads llama_cpp,
24
+ torch, transformers or peft. Those arrive through extras and are imported by
25
+ the runner that needs them:
26
+
27
+ | extra | provides |
28
+ |---|---|
29
+ | `hugpy-engine[gguf]` | in-process GGUF inference via `llama-cpp-python` |
30
+ | `hugpy-engine[transformers]` | the transformers text-generation runner |
31
+ | `hugpy-engine[finetune]` | PEFT adapter loading |
32
+ | `hugpy-engine[index]` | the optional Postgres model index |
33
+
34
+ The default backend is the in-process `LocalBackend` (`hugpy_engine.backends`),
35
+ built from the engine's own catalog, allocation, native-engine and dispatch
36
+ modules; `configure_backend(...)` / `backend_scope(...)` swap in another
37
+ `InferenceBackend` (a fake in tests, a remote proxy in a thin client).
38
+
39
+ ```python
40
+ from hugpy_engine import query_sync
41
+
42
+ reply = query_sync("Explain tensor parallelism", model_key="my-model")
43
+ ```
44
+
45
+ Async callers use `await query(...)`. `stream_query(...)` yields engine events;
46
+ `query_result(...)` retains request ID, finish reason, usage, and timings.
47
+
48
+ ## Seams (what the engine asks of the packages above it)
49
+
50
+ - `hugpy_engine.placement` — Protocols + `get_*()/set_*()` providers for
51
+ everything the engine asks about the fleet (worker registry and key forms,
52
+ worker HTTP transport/breaker, eviction telemetry ledger, blocklist, model
53
+ metrics, priority groups). Null defaults mean "no fleet"; `hugpy_fleet`
54
+ implements them and the server wires them.
55
+ - `hugpy_engine.tasks` — the task-runner registry. Media and video runners
56
+ plug in with `register_task(task, runner=..., build_request=...,
57
+ frameworks=(...), extra=..., source=...)`; `FRAMEWORK_RUNNERS`,
58
+ `MODEL_REQUEST_BUILDERS` and `KNOWN_TASKS_REGISTRY` in
59
+ `hugpy_engine.resolvers.categories` are live views over it. Packages may
60
+ also expose a `hugpy_engine.tasks` entry point (a zero-arg register
61
+ function); the engine loads those on first use.
62
+ - `hugpy_engine.catalog_bridge` — installs the engine's registry as
63
+ `hugpy_storage`'s catalog source, the hot cache as its serve-path hook, and
64
+ refreshes discovery on `hugpy_control.bus` `catalog.changed` events.
65
+ `LocalBackend` installs it lazily.
66
+ - `hugpy_engine.name_match` — the pure eliminate-then-rank name pipeline
67
+ (`resolve_name`, `Candidate`) that `assure_model_key` uses; the oracle
68
+ re-exports it.
@@ -0,0 +1,174 @@
1
+ # Allocation modes — where your model's memory lives
2
+
3
+ Every model gets **one of five modes** that says how its weights (and KV
4
+ context) are placed across the GPU (VRAM) and system RAM. Pick the intent; the
5
+ fleet does the math.
6
+
7
+ > **The promise:** a *slow* mode you chose is yours; a model with *no working
8
+ > mode at all* is our bug. The default never OOMs — a blank model serves.
9
+
10
+ ## The five modes
11
+
12
+ | mode | what it does | when it refuses ("bust") |
13
+ |---|---|---|
14
+ | **gpu-only** | Every layer on the GPU, no spill. Fastest when it fits. | Won't fit the GPU (even after evicting idle residents) → refuse. Never a half-load. |
15
+ | **ram-only** | Everything in system RAM; the GPU is never touched (binds the CPU even when a GPU is present). | Won't fit RAM → refuse. |
16
+ | **max-gpu** | *The default.* As much GPU as is available and needed, the rest spills to RAM. Zero knobs — "use my GPU, spill the rest". | Only when GPU + RAM together can't hold it. |
17
+ | **max-ram** | Mirror of max-gpu: fill RAM first, only the overflow rides the GPU. For keeping cards free while big models stay warm. | Only when RAM + GPU together can't hold it. |
18
+ | **explicit** | You set the targets: a VRAM and/or RAM budget, a **leniency %**, and a **priority device** (`gpu` default, or `ram`), plus the tolerance bands/priority you already know. | Can't hit the target *even at the loosened floor* → refuse. |
19
+
20
+ `max-gpu` and `max-ram` are internally explicit-with-generous-leniency — but
21
+ they stay flat, named modes on purpose: simple intent gets a simple pick; the
22
+ moment you want to tune numbers, you reach for `explicit`.
23
+
24
+ ## Leniency — how much off-device you tolerate
25
+
26
+ **N% leniency = up to N% of the MODEL may land off its ideal device before we
27
+ refuse.**
28
+
29
+ Example: `100% GPU target + 30% leniency`
30
+ - Try 100% on GPU first.
31
+ - Under pressure, degrade step by step — 90/10, 80/20 … — down to the **floor:
32
+ 70% GPU / 30% RAM**.
33
+ - If even 70/30 won't fit, the load refuses **honestly**, naming the mode and
34
+ the floor (e.g. *"explicit: even the loosened floor won't fit — target 100%
35
+ of the model on GPU with 30% leniency gives a floor of 70% …"*).
36
+
37
+ No leniency set = exact target or refuse (no undeclared tolerance is ever
38
+ assumed). Degradation only happens under real contention — a model that fits
39
+ at its target always gets its target.
40
+
41
+ ## Engine coverage
42
+
43
+ Two transformers families load differently, so they honor the modes by
44
+ different mechanisms:
45
+
46
+ - **transformers TEXT** (`AutoModelFor*`: the causal-LM generator, the flan /
47
+ seq2seq / summarization back-ends, the vision-language coder) place weights
48
+ with **accelerate** — `device_map="auto"` + a `max_memory` budget map. Every
49
+ mode maps onto that budget map.
50
+ - **diffusers** (text-to-image / image-to-image pipelines) do **not** take
51
+ `device_map`/`max_memory`. They spill via diffusers' own **CPU-offload API**
52
+ instead (see the note below the table).
53
+
54
+ | mode | GGUF (llama.cpp) | transformers TEXT (accelerate) | diffusers (image) |
55
+ |---|---|---|---|
56
+ | gpu-only | ✅ | ✅ whole model on the card (no CPU budget) | ✅ `.to(cuda)` |
57
+ | ram-only | ✅ | ✅ CPU placement (gpu budget 0) | ✅ `enable_sequential_cpu_offload()` |
58
+ | max-gpu | ✅ | ✅ accelerate fit-and-spill | ✅ `.to(cuda)` (auto/default) |
59
+ | max-ram | ✅ | ✅ RAM-priority `max_memory` [^purerram] | ✅ `enable_model_cpu_offload()` |
60
+ | explicit | ✅ | ❌ GGUF-only (banded leniency floor has no transformers analogue) | ❌ GGUF-only (same rationale) |
61
+
62
+ [^purerram]: **Pure-RAM caveat.** Without a resolved `model_need_bytes` the
63
+ transformers loaders take the SAFE pure-RAM path (the whole model on the CPU) —
64
+ they do not yet compute the RAM-first / GPU-overflow split. A follow-up may add
65
+ overflow-to-GPU sizing; until then max-ram on a non-GGUF text model means "RAM
66
+ first" without spilling the remainder onto the card when the size is unknown.
67
+
68
+ **Loader-level vs central gate.** Slice C wired the transformers gap loaders to
69
+ the spill seam, so at the WORKER a transformers text model honors max-ram
70
+ (RAM-priority `max_memory`) and the gpu-only / ram-only / max-gpu placement
71
+ intents. **Central now accepts max-ram for non-GGUF models** (operator-approved
72
+ 2026-07-24): the engine gate at `/assign` was opened for the max-ram *mode*
73
+ because all three gap loaders honor it (text via `transformers_max_memory`'s
74
+ RAM-priority branch, imagegen via `enable_model_cpu_offload`). **`explicit`
75
+ stays GGUF-only at the central gate** — its banded leniency floor is a
76
+ llama.cpp concept with no transformers analogue, so admitting it would break the
77
+ mode's promise. The remaining GGUF-only knobs (`gpu_mem_gib` / `cpu_mem_gib` /
78
+ `threads` / `tensor_split` / the t21 bands / MoE `n_cpu_moe`) stay refused for
79
+ non-GGUF exactly as before.
80
+
81
+ **diffusers offload mechanism.** A diffusers pipeline can't shard by a
82
+ `max_memory` map, so the honest spill is diffusers' offload API, chosen from the
83
+ SAME placement seam (`spill.n_gpu_layers_intent` / `alloc_mode_env`):
84
+ - CPU-leaning intent (ram-only / `n_gpu_layers` `off`/`0`) →
85
+ `enable_sequential_cpu_offload()` — submodules stream one at a time,
86
+ smallest VRAM footprint (slowest).
87
+ - max-ram → `enable_model_cpu_offload()` — whole submodules ride RAM, pulled to
88
+ the card only while active (big model, one consumer GPU, no OOM).
89
+ - gpu-only / auto / no intent → today's `.to(cuda)` — byte-identical when the
90
+ seam is silent.
91
+ - A pipeline class without the requested offload method is a **genuine
92
+ capability gap**: it is logged once and falls back to `.to(cuda)` rather than
93
+ silently ignoring the mode.
94
+
95
+ `explicit`'s numeric leniency floor is a llama.cpp-only concept with no
96
+ transformers/diffusers analogue, so `explicit` is refused for non-GGUF at the
97
+ central gate and is not a reachable live path for transformers. (Were the gate
98
+ opened, a transformers/diffusers model under explicit would get only the
99
+ budgeted fit-and-spill, not the banded degrade-to-floor — which is exactly why
100
+ the mode's promise can't be kept there and the gate stays closed.)
101
+
102
+ ## MoE models — the expert split (measured, on by default)
103
+
104
+ A Mixture-of-Experts GGUF is mostly **cold expert tensors**: on
105
+ Qwen3-Coder-Next (80B-A3B class, 512 experts / 10 used per token) the expert
106
+ FFN tensors are ~43.6 GiB of the ~45.1 GiB file — the always-hot
107
+ attention/shared/KV share is only ~1.5 GiB. Splitting **by kind of bytes**
108
+ instead of by whole layers changes everything:
109
+
110
+ | plan (ae / RTX 3090, coder-next) | tok/s | VRAM |
111
+ |---|---|---|
112
+ | naive layer split (autofit, 17/48 layers) | ~15.2 | 16.6 GiB |
113
+ | **MoE split** (`n_gpu_layers=-1` + `--n-cpu-moe 999`) | **~24.1** | **3.2 GiB** |
114
+
115
+ +59% throughput AND 5× less VRAM — a strict improvement, so it is the
116
+ **default** for the hybrid case:
117
+
118
+ - **Auto policy** (no knobs): a detected-MoE GGUF whose whole file does **not**
119
+ fit the card (exactly when autofit would have produced a partial layer
120
+ split) serves with all layers on the GPU and the expert tensors on CPU.
121
+ *Whole model fits → fully on GPU, unchanged. Dense model → byte-identical to
122
+ before (no expert tensors, nothing to split).* Detection is the GGUF header
123
+ the loader already parses: `expert_count > 0` is a MoE; the expert tensors
124
+ are the `blk.<i>.ffn_*_exps.*` names (`_exps` is the per-tensor expert bit).
125
+ - **The knob**: `n_cpu_moe` — a first-class serve override / spill key (like
126
+ `n_gpu_layers`): the number of MoE layers whose expert tensors stay on CPU
127
+ (`999` = all, `0` = experts on GPU). An explicit value always wins over the
128
+ auto policy, and explicit `n_gpu_layers` designations (gpu-only/ram-only or
129
+ a layer count) always win over the auto split.
130
+ - **Fit/feasibility price the split**: probes, the ⭐ boot load, admission and
131
+ central's feasibility judge a MoE by its **non-expert GPU share** (+KV) when
132
+ the split governs — a 41.6 GB MoE on an empty 24 GB card *fits* (needs
133
+ ~3 GiB of VRAM); its expert bytes are checked against host RAM instead.
134
+ - **Engine**: needs a native `llama-server` with `--n-cpu-moe` (probed once;
135
+ an older binary or the `llama_cpp.server` python fallback degrades to the
136
+ layer-split behavior with one log line — never a crash).
137
+
138
+ **Coexistence note**: ae's worker unit currently carries
139
+ `LLAMA_ARG_N_CPU_MOE=999` (the transition-era env hack that proved the win).
140
+ Explicit `--n-cpu-moe` argv beats the env in llama-server, so this feature is
141
+ correct with or without it — the env line can be removed once this ships.
142
+
143
+ ## Legacy names (the honest rename)
144
+
145
+ Old controls were misleadingly named; existing settings keep working — old
146
+ names are accepted on input, resolved, and never written back:
147
+
148
+ | old name / setting | is now | why |
149
+ |---|---|---|
150
+ | **autofit** ({} / no knobs) | **max-gpu** | it always was "as much GPU as fits, spill the rest" |
151
+ | **Max GPU** (`n_gpu_layers: -1`) | **gpu-only** | it was all-or-OOM, not "maximize" — now it refuses instead of crashing |
152
+ | **CPU only** (`n_gpu_layers: "off"`) | **ram-only** | RAM is what it actually binds |
153
+ | **budget** / **bands** (chaos) | **explicit** | a budget/band IS an explicit allocation |
154
+
155
+ Nothing on the wire changed for these three: `gpu-only` still rides
156
+ `n_gpu_layers: -1`, `ram-only` still rides `"off"`, `max-gpu` is still `{}`.
157
+ Your `serve_overrides.json` is never rewritten — the mode is **derived at read
158
+ time** (`-1` → gpu-only, `0/"off"` → ram-only, budgets/bands → explicit, unset
159
+ → max-gpu).
160
+
161
+ ## Old workers
162
+
163
+ `max-ram` and `explicit` ride new spill keys (`alloc_mode`, `leniency_pct`,
164
+ `priority_device`), and the MoE `n_cpu_moe` knob rides the same gate.
165
+ Central only emits them to workers running **0.1.203+**;
166
+ an older worker gets max-gpu (autofit) for that request and the downgrade is
167
+ logged — your persisted choice is kept and applies the moment the worker
168
+ updates. A selected mode is never a silent dead knob.
169
+
170
+ ## Protection is unchanged
171
+
172
+ Modes reorder *placement*, never protection: 🔒 static residents and models
173
+ actively answering are never evicted to make room, whatever mode the incoming
174
+ model carries.
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "setuptools-scm>=8"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hugpy-engine"
7
+ dynamic = ["version"]
8
+ description = "Hugpy engine: model discovery and classification, allocation and eviction, native llama.cpp and GGUF runners, and the prompt-to-reply inference path with task and placement seams"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "LicenseRef-Proprietary"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "putkoff", email = "support@hugpy.ai" }]
14
+ keywords = [
15
+ "hugpy",
16
+ "llm",
17
+ "self-hosted",
18
+ "inference",
19
+ "llama.cpp",
20
+ "gguf",
21
+ "transformers",
22
+ "model-serving",
23
+ ]
24
+ classifiers = [
25
+ "Development Status :: 3 - Alpha",
26
+ "Intended Audience :: Developers",
27
+ "Operating System :: OS Independent",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3 :: Only",
30
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
31
+ ]
32
+ dependencies = [
33
+ "hugpy-platform",
34
+ "hugpy-control",
35
+ "hugpy-storage",
36
+ "abstract-essentials",
37
+ "pydantic>=2",
38
+ "httpx",
39
+ "requests",
40
+ "huggingface-hub",
41
+ "psutil",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://hugpy.ai"
46
+ Documentation = "https://github.com/hugpy/hugpy/blob/main/py/inference/hugpy_engine/README.md"
47
+ Repository = "https://github.com/hugpy/hugpy"
48
+ Source = "https://github.com/hugpy/hugpy/tree/main/py/inference/hugpy_engine"
49
+ Issues = "https://github.com/hugpy/hugpy/issues"
50
+ Changelog = "https://github.com/hugpy/hugpy/releases"
51
+ Architecture = "https://github.com/hugpy/hugpy/blob/main/PARTITION.md"
52
+
53
+ [project.optional-dependencies]
54
+ # In-process GGUF inference (LlamaCppPythonRunner). Without it the engine
55
+ # still serves GGUF models through llama-server slots or remote workers.
56
+ gguf = ["llama-cpp-python"]
57
+ # transformers text-generation runner (DeepCoderChatRunner).
58
+ transformers = ["torch", "transformers", "accelerate", "safetensors"]
59
+ # PEFT adapter loading on top of a transformers base.
60
+ finetune = ["peft"]
61
+ # Postgres-backed model index (HUGPY_REGISTRY_DB=pg); inert without it.
62
+ index = ["psycopg[binary]"]
63
+ test = ["pytest>=8", "pytest-timeout"]
64
+
65
+ [project.scripts]
66
+ hugpy-serve = "hugpy_engine.serve.serve_cli:main"
67
+
68
+ [tool.setuptools.packages.find]
69
+ where = ["src"]
70
+
71
+ [tool.setuptools.package-data]
72
+ hugpy_engine = ["py.typed"]
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Lockstep workspace version (2026-09-22): every in-tree hugpy-* distribution
76
+ # takes ONE version from the workspace git tag (vX.Y.Z at the repo root), so a
77
+ # release is a tag and a build always carries the commit it came from
78
+ # (X.Y.Z.devN+g<sha>[.dirty] between tags). A checkout without git metadata
79
+ # builds as 0.0.0+unknown, which central refuses to advertise to workers.
80
+ [tool.setuptools_scm]
81
+ root = "../../.."
82
+ fallback_version = "0.0.0+unknown"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,163 @@
1
+ """Standalone Hugpy inference boundary.
2
+
3
+ Prompt/messages -> model discovery and resolution -> RAM/VRAM allocation ->
4
+ engine/runner selection -> generation -> streamed events or a completed reply.
5
+
6
+ The package imports CPU-only: llama_cpp / torch / transformers / peft load
7
+ lazily inside the runner that needs them (``pip install hugpy-engine[gguf]``,
8
+ ``[transformers]``, ``[finetune]``). Engine-owned contracts (task capability
9
+ map, catalog vocabulary, manifest helpers, wire schemas, the placement and
10
+ task-plugin seams) are exposed lazily via ``__getattr__`` so ``import
11
+ hugpy_engine`` stays light.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import importlib
17
+
18
+ try:
19
+ from importlib.metadata import version as _package_version
20
+
21
+ __version__ = _package_version("hugpy-engine")
22
+ except Exception:
23
+ __version__ = "0.0.0+unknown" # source tree without metadata
24
+
25
+ from .allocation import (
26
+ allocation_spill,
27
+ choose_allocation,
28
+ feasible_allocations,
29
+ plan_admission,
30
+ resource_status,
31
+ )
32
+ from .backend import InferenceBackend, backend_scope, configure_backend, get_backend
33
+ from .catalog import get_model, list_models, refresh_models, resolve_model, text_chat_task
34
+ from .contracts import ModelDescriptor, QueryError, QueryResult
35
+ from .engines import (
36
+ build_native_engine,
37
+ evict_llama_runner,
38
+ install_native_engine,
39
+ llama_runner,
40
+ loaded_llama_runners,
41
+ native_engine_binaries,
42
+ native_engine_status,
43
+ )
44
+ from .query import query, query_result, query_result_sync, query_sync, stream_query
45
+ from .runtime import (
46
+ clear_models,
47
+ evict_model,
48
+ execute_request,
49
+ loaded_models,
50
+ loading_models,
51
+ resolve_request,
52
+ runner_for,
53
+ stream_chat_request,
54
+ stream_request,
55
+ supported_tasks,
56
+ )
57
+
58
+ # Engine-owned contracts and seams, resolved on first attribute access
59
+ # (PEP 562) so the package import never touches discovery or pydantic.
60
+ _LAZY = {
61
+ # task capability map (consumed by fleet, oracle, server)
62
+ "TASK_DEPS": ("hugpy_engine.task_deps", "TASK_DEPS"),
63
+ "task_capabilities": ("hugpy_engine.task_deps", "task_capabilities"),
64
+ # catalog vocabulary
65
+ "HF_TASK_TO_TASKS": ("hugpy_engine.categories", "HF_TASK_TO_TASKS"),
66
+ "RUNNER_PAIRS": ("hugpy_engine.categories", "RUNNER_PAIRS"),
67
+ "TASK_DEFAULTS": ("hugpy_engine.categories", "TASK_DEFAULTS"),
68
+ "MEDIA_DEFAULTS": ("hugpy_engine.categories", "MEDIA_DEFAULTS"),
69
+ # manifest helpers
70
+ "load_manifest": ("hugpy_engine.manifest", "load_manifest"),
71
+ "load_manifest_or_empty": ("hugpy_engine.manifest", "load_manifest_or_empty"),
72
+ "save_manifest": ("hugpy_engine.manifest", "save_manifest"),
73
+ "key_for_hub_id": ("hugpy_engine.manifest", "key_for_hub_id"),
74
+ "upsert_model": ("hugpy_engine.manifest", "upsert_model"),
75
+ # seams
76
+ "placement": ("hugpy_engine.placement", None),
77
+ "tasks": ("hugpy_engine.tasks", None),
78
+ "register_task": ("hugpy_engine.tasks", "register_task"),
79
+ "runner_for_task": ("hugpy_engine.tasks", "runner_for_task"),
80
+ "request_builder_for_task": ("hugpy_engine.tasks", "request_builder_for_task"),
81
+ "catalog_bridge": ("hugpy_engine.catalog_bridge", None),
82
+ "LocalBackend": ("hugpy_engine.backends.local", "LocalBackend"),
83
+ # wire schema modules (pydantic bodies shared with the HTTP layer)
84
+ "wire": ("hugpy_engine.wire", None),
85
+ "schemas": ("hugpy_engine.schemas", None),
86
+ }
87
+
88
+
89
+ def __getattr__(name: str):
90
+ target = _LAZY.get(name)
91
+ if target is None:
92
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
93
+ module = importlib.import_module(target[0])
94
+ value = module if target[1] is None else getattr(module, target[1])
95
+ globals()[name] = value
96
+ return value
97
+
98
+
99
+ def __dir__():
100
+ return sorted(set(globals()) | set(_LAZY))
101
+
102
+
103
+ __all__ = [
104
+ "HF_TASK_TO_TASKS",
105
+ "InferenceBackend",
106
+ "LocalBackend",
107
+ "MEDIA_DEFAULTS",
108
+ "ModelDescriptor",
109
+ "QueryError",
110
+ "QueryResult",
111
+ "RUNNER_PAIRS",
112
+ "TASK_DEFAULTS",
113
+ "TASK_DEPS",
114
+ "allocation_spill",
115
+ "backend_scope",
116
+ "build_native_engine",
117
+ "catalog_bridge",
118
+ "choose_allocation",
119
+ "clear_models",
120
+ "configure_backend",
121
+ "evict_llama_runner",
122
+ "evict_model",
123
+ "execute_request",
124
+ "feasible_allocations",
125
+ "get_backend",
126
+ "get_model",
127
+ "install_native_engine",
128
+ "key_for_hub_id",
129
+ "list_models",
130
+ "llama_runner",
131
+ "load_manifest",
132
+ "load_manifest_or_empty",
133
+ "loaded_llama_runners",
134
+ "loaded_models",
135
+ "loading_models",
136
+ "native_engine_binaries",
137
+ "native_engine_status",
138
+ "placement",
139
+ "plan_admission",
140
+ "query",
141
+ "query_result",
142
+ "query_result_sync",
143
+ "query_sync",
144
+ "refresh_models",
145
+ "register_task",
146
+ "request_builder_for_task",
147
+ "resolve_model",
148
+ "resolve_request",
149
+ "resource_status",
150
+ "runner_for",
151
+ "runner_for_task",
152
+ "save_manifest",
153
+ "schemas",
154
+ "stream_chat_request",
155
+ "stream_query",
156
+ "stream_request",
157
+ "supported_tasks",
158
+ "task_capabilities",
159
+ "tasks",
160
+ "text_chat_task",
161
+ "upsert_model",
162
+ "wire",
163
+ ]