hugpy-engine 0.2.6__tar.gz → 0.3.1__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_engine-0.3.1/PKG-INFO +22 -0
- hugpy_engine-0.3.1/README.md +3 -0
- hugpy_engine-0.3.1/pyproject.toml +41 -0
- hugpy_engine-0.3.1/src/hugpy_engine/__init__.py +49 -0
- hugpy_engine-0.3.1/src/hugpy_engine/config/main.py +84 -0
- hugpy_engine-0.3.1/src/hugpy_engine/config/models/models_config.py +190 -0
- hugpy_engine-0.3.1/src/hugpy_engine/config/models/models_default.py +150 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/dispatch/__init__.py +1 -2
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/dispatch/dispatch.py +43 -1
- hugpy_engine-0.3.1/src/hugpy_engine/llama/runners/fitevict_runner.py +197 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/placement.py +15 -1
- hugpy_engine-0.3.1/src/hugpy_engine/resolvers/__init__.py +22 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/resolvers/categories/builders.py +7 -1
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/resolvers/categories/frameworks.py +11 -3
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/resolvers/model_resolver.py +58 -176
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/chat_schemas.py +1 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/event_schemas.py +1 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/runner_schemas.py +1 -1
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/task_schemas.py +1 -0
- hugpy_engine-0.3.1/src/hugpy_engine.egg-info/PKG-INFO +22 -0
- hugpy_engine-0.3.1/src/hugpy_engine.egg-info/SOURCES.txt +38 -0
- hugpy_engine-0.3.1/src/hugpy_engine.egg-info/requires.txt +3 -0
- hugpy_engine-0.2.6/LICENSE +0 -41
- hugpy_engine-0.2.6/PKG-INFO +0 -116
- hugpy_engine-0.2.6/README.md +0 -68
- hugpy_engine-0.2.6/docs/ALLOCATION-MODES.md +0 -174
- hugpy_engine-0.2.6/pyproject.toml +0 -82
- hugpy_engine-0.2.6/src/hugpy_engine/__init__.py +0 -163
- hugpy_engine-0.2.6/src/hugpy_engine/alloc_modes.py +0 -1342
- hugpy_engine-0.2.6/src/hugpy_engine/allocation.py +0 -68
- hugpy_engine-0.2.6/src/hugpy_engine/apis/call_api.py +0 -149
- hugpy_engine-0.2.6/src/hugpy_engine/apis/get_module.py +0 -649
- hugpy_engine-0.2.6/src/hugpy_engine/apis/reclassify.py +0 -238
- hugpy_engine-0.2.6/src/hugpy_engine/apis/reconcile.py +0 -628
- hugpy_engine-0.2.6/src/hugpy_engine/apis/serve/__init__.py +0 -22
- hugpy_engine-0.2.6/src/hugpy_engine/apis/serve/serve.py +0 -33
- hugpy_engine-0.2.6/src/hugpy_engine/apis/serve/serve_cli.py +0 -9
- hugpy_engine-0.2.6/src/hugpy_engine/apis/systemd_units.py +0 -9
- hugpy_engine-0.2.6/src/hugpy_engine/backend.py +0 -86
- hugpy_engine-0.2.6/src/hugpy_engine/backends/__init__.py +0 -5
- hugpy_engine-0.2.6/src/hugpy_engine/backends/local.py +0 -163
- hugpy_engine-0.2.6/src/hugpy_engine/catalog.py +0 -99
- hugpy_engine-0.2.6/src/hugpy_engine/catalog_bridge.py +0 -230
- hugpy_engine-0.2.6/src/hugpy_engine/chat_context/__init__.py +0 -27
- hugpy_engine-0.2.6/src/hugpy_engine/chat_context/chat_context.py +0 -223
- hugpy_engine-0.2.6/src/hugpy_engine/chat_context/context_budget.py +0 -223
- hugpy_engine-0.2.6/src/hugpy_engine/chat_context/unbounded.py +0 -55
- hugpy_engine-0.2.6/src/hugpy_engine/config/main.py +0 -286
- hugpy_engine-0.2.6/src/hugpy_engine/config/models/model_meta.py +0 -206
- hugpy_engine-0.2.6/src/hugpy_engine/config/models/models_config.py +0 -1838
- hugpy_engine-0.2.6/src/hugpy_engine/config/models/models_default.py +0 -196
- hugpy_engine-0.2.6/src/hugpy_engine/config/models/models_dict.py +0 -73
- hugpy_engine-0.2.6/src/hugpy_engine/contracts.py +0 -44
- hugpy_engine-0.2.6/src/hugpy_engine/dispatch/acquire.py +0 -59
- hugpy_engine-0.2.6/src/hugpy_engine/dispatch/activity.py +0 -122
- hugpy_engine-0.2.6/src/hugpy_engine/draft_models.py +0 -72
- hugpy_engine-0.2.6/src/hugpy_engine/engines.py +0 -44
- hugpy_engine-0.2.6/src/hugpy_engine/eviction.py +0 -662
- hugpy_engine-0.2.6/src/hugpy_engine/fit/__init__.py +0 -28
- hugpy_engine-0.2.6/src/hugpy_engine/fit/flex.py +0 -709
- hugpy_engine-0.2.6/src/hugpy_engine/fit/gguf_need.py +0 -203
- hugpy_engine-0.2.6/src/hugpy_engine/fit/plan.py +0 -769
- hugpy_engine-0.2.6/src/hugpy_engine/generate/__init__.py +0 -16
- hugpy_engine-0.2.6/src/hugpy_engine/generate/coder.py +0 -694
- hugpy_engine-0.2.6/src/hugpy_engine/generate/config.py +0 -258
- hugpy_engine-0.2.6/src/hugpy_engine/generate/generate_runner.py +0 -323
- hugpy_engine-0.2.6/src/hugpy_engine/gguf_election.py +0 -259
- hugpy_engine-0.2.6/src/hugpy_engine/llama/__init__.py +0 -23
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/__init__.py +0 -23
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/chat_runner.py +0 -266
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/get.py +0 -852
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/__init__.py +0 -9
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/base_runner.py +0 -660
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/ccp_runner.py +0 -351
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/imports/__init__.py +0 -25
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/imports/config.py +0 -16
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/imports/constants.py +0 -38
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/imports/init_imports.py +0 -6
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/imports/utils.py +0 -64
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/python_runner.py +0 -511
- hugpy_engine-0.2.6/src/hugpy_engine/llama/runners/src/shard_server.py +0 -341
- hugpy_engine-0.2.6/src/hugpy_engine/llama/serve.py +0 -9
- hugpy_engine-0.2.6/src/hugpy_engine/manifest.py +0 -53
- hugpy_engine-0.2.6/src/hugpy_engine/model_classifier.py +0 -382
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/__init__.py +0 -108
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/client.py +0 -101
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/planner/__init__.py +0 -5
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/planner/compute.py +0 -714
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/planner/store.py +0 -466
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/query_registry.py +0 -1052
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/repositories.py +0 -302
- hugpy_engine-0.2.6/src/hugpy_engine/model_index/service.py +0 -1025
- hugpy_engine-0.2.6/src/hugpy_engine/native/__init__.py +0 -19
- hugpy_engine-0.2.6/src/hugpy_engine/native/build.py +0 -98
- hugpy_engine-0.2.6/src/hugpy_engine/native/fetch.py +0 -194
- hugpy_engine-0.2.6/src/hugpy_engine/native/resolve.py +0 -352
- hugpy_engine-0.2.6/src/hugpy_engine/output_repair.py +0 -212
- hugpy_engine-0.2.6/src/hugpy_engine/peft_adapters.py +0 -293
- hugpy_engine-0.2.6/src/hugpy_engine/query.py +0 -191
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/__init__.py +0 -26
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/allocator.py +0 -126
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/device_placement.py +0 -212
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/groups.py +0 -651
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/model_dict_resolver.py +0 -23
- hugpy_engine-0.2.6/src/hugpy_engine/resolvers/remote.py +0 -4606
- hugpy_engine-0.2.6/src/hugpy_engine/routing_diagnostics.py +0 -265
- hugpy_engine-0.2.6/src/hugpy_engine/runtime.py +0 -84
- hugpy_engine-0.2.6/src/hugpy_engine/serve/__init__.py +0 -57
- hugpy_engine-0.2.6/src/hugpy_engine/serve/chunksum_verify.py +0 -213
- hugpy_engine-0.2.6/src/hugpy_engine/serve/hot_cache.py +0 -1014
- hugpy_engine-0.2.6/src/hugpy_engine/serve/load_failure.py +0 -271
- hugpy_engine-0.2.6/src/hugpy_engine/serve/model_cache.py +0 -218
- hugpy_engine-0.2.6/src/hugpy_engine/serve/overrides.py +0 -1156
- hugpy_engine-0.2.6/src/hugpy_engine/serve/policy.py +0 -45
- hugpy_engine-0.2.6/src/hugpy_engine/serve/profiles.py +0 -417
- hugpy_engine-0.2.6/src/hugpy_engine/serve/serve.py +0 -930
- hugpy_engine-0.2.6/src/hugpy_engine/serve/serve_cli.py +0 -79
- hugpy_engine-0.2.6/src/hugpy_engine/serve/slot_agent.py +0 -2626
- hugpy_engine-0.2.6/src/hugpy_engine/serve/slots.py +0 -882
- hugpy_engine-0.2.6/src/hugpy_engine/serve/supervisor.py +0 -148
- hugpy_engine-0.2.6/src/hugpy_engine/serve/tf_child.py +0 -553
- hugpy_engine-0.2.6/src/hugpy_engine/spill.py +0 -1749
- hugpy_engine-0.2.6/src/hugpy_engine/task_deps.py +0 -67
- hugpy_engine-0.2.6/src/hugpy_engine/utils/json_scavenge.py +0 -111
- hugpy_engine-0.2.6/src/hugpy_engine/utils/no_think.py +0 -325
- hugpy_engine-0.2.6/src/hugpy_engine/wire/chat_schemas.py +0 -73
- hugpy_engine-0.2.6/src/hugpy_engine/wire/config_schemas.py +0 -31
- hugpy_engine-0.2.6/src/hugpy_engine/wire/install_schemas.py +0 -17
- hugpy_engine-0.2.6/src/hugpy_engine/wire/model_schemas.py +0 -43
- hugpy_engine-0.2.6/src/hugpy_engine/wire/request_schemas.py +0 -13
- hugpy_engine-0.2.6/src/hugpy_engine/wire/specs_schemas.py +0 -9
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/PKG-INFO +0 -116
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/SOURCES.txt +0 -220
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/entry_points.txt +0 -2
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/requires.txt +0 -28
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/scm_file_list.json +0 -216
- hugpy_engine-0.2.6/src/hugpy_engine.egg-info/scm_version.json +0 -8
- hugpy_engine-0.2.6/tests/conftest.py +0 -41
- hugpy_engine-0.2.6/tests/known_good/test_call_queue_contract.py +0 -357
- hugpy_engine-0.2.6/tests/known_good/test_dependency_acquisition_contract.py +0 -252
- hugpy_engine-0.2.6/tests/known_good/test_evict_fit_contract.py +0 -198
- hugpy_engine-0.2.6/tests/known_good/test_key_equivalence_contract.py +0 -99
- hugpy_engine-0.2.6/tests/known_good/test_missing_dependency_contract.py +0 -116
- hugpy_engine-0.2.6/tests/known_good/test_model_format_contract.py +0 -193
- hugpy_engine-0.2.6/tests/known_good/test_model_key_contract.py +0 -318
- hugpy_engine-0.2.6/tests/known_good/test_plan_fit_contract.py +0 -765
- hugpy_engine-0.2.6/tests/known_good/test_politeness_retired_contract.py +0 -65
- hugpy_engine-0.2.6/tests/known_good/test_prefill_progress_contract.py +0 -99
- hugpy_engine-0.2.6/tests/known_good/test_relay_gate_allocation_contract.py +0 -111
- hugpy_engine-0.2.6/tests/known_good/test_relay_stream_stall_contract.py +0 -279
- hugpy_engine-0.2.6/tests/known_good/test_remote_error_structure_contract.py +0 -133
- hugpy_engine-0.2.6/tests/known_good/test_response_format_contract.py +0 -104
- hugpy_engine-0.2.6/tests/known_good/test_slot_seat_admission_contract.py +0 -196
- hugpy_engine-0.2.6/tests/known_good/test_slot_serialization_contract.py +0 -159
- hugpy_engine-0.2.6/tests/test_admission_gate_and_diagnostics.py +0 -178
- hugpy_engine-0.2.6/tests/test_allocation_accuracy.py +0 -257
- hugpy_engine-0.2.6/tests/test_budget_bar_spec.py +0 -179
- hugpy_engine-0.2.6/tests/test_catalog_bridge.py +0 -100
- hugpy_engine-0.2.6/tests/test_central_cap_gating.py +0 -295
- hugpy_engine-0.2.6/tests/test_chat_request_caller.py +0 -40
- hugpy_engine-0.2.6/tests/test_chat_role_alternation.py +0 -563
- hugpy_engine-0.2.6/tests/test_chat_tool_messages_schema.py +0 -113
- hugpy_engine-0.2.6/tests/test_chunksum_verify.py +0 -231
- hugpy_engine-0.2.6/tests/test_comms_foundations.py +0 -113
- hugpy_engine-0.2.6/tests/test_ctx_fit.py +0 -296
- hugpy_engine-0.2.6/tests/test_ctx_range_flex.py +0 -31
- hugpy_engine-0.2.6/tests/test_device_placement.py +0 -151
- hugpy_engine-0.2.6/tests/test_engine_boundary.py +0 -98
- hugpy_engine-0.2.6/tests/test_engine_persisted_resolve.py +0 -295
- hugpy_engine-0.2.6/tests/test_evict_releases_transformers_registry.py +0 -51
- hugpy_engine-0.2.6/tests/test_eviction_flow.py +0 -411
- hugpy_engine-0.2.6/tests/test_feasible_distribution_engine.py +0 -89
- hugpy_engine-0.2.6/tests/test_gguf_election.py +0 -301
- hugpy_engine-0.2.6/tests/test_gguf_sharded_effective.py +0 -162
- hugpy_engine-0.2.6/tests/test_hot_cache.py +0 -357
- hugpy_engine-0.2.6/tests/test_image_task_classification.py +0 -405
- hugpy_engine-0.2.6/tests/test_import_policy.py +0 -107
- hugpy_engine-0.2.6/tests/test_load_failure_reason.py +0 -188
- hugpy_engine-0.2.6/tests/test_mmproj_effective_bytes.py +0 -79
- hugpy_engine-0.2.6/tests/test_model_aliases.py +0 -86
- hugpy_engine-0.2.6/tests/test_no_makeroom.py +0 -316
- hugpy_engine-0.2.6/tests/test_no_think.py +0 -329
- hugpy_engine-0.2.6/tests/test_peft_adapter_load.py +0 -379
- hugpy_engine-0.2.6/tests/test_per_device_slot.py +0 -125
- hugpy_engine-0.2.6/tests/test_per_request_alloc.py +0 -91
- hugpy_engine-0.2.6/tests/test_placement_defaults.py +0 -104
- hugpy_engine-0.2.6/tests/test_planner_encroachment.py +0 -39
- hugpy_engine-0.2.6/tests/test_planner_worker_totals.py +0 -20
- hugpy_engine-0.2.6/tests/test_profile_worker_base.py +0 -25
- hugpy_engine-0.2.6/tests/test_provision_failure_honesty.py +0 -120
- hugpy_engine-0.2.6/tests/test_ram_residents_plan.py +0 -50
- hugpy_engine-0.2.6/tests/test_registry_attribution.py +0 -144
- hugpy_engine-0.2.6/tests/test_registry_symmetry.py +0 -172
- hugpy_engine-0.2.6/tests/test_resolve_routing_key_reconcile.py +0 -92
- hugpy_engine-0.2.6/tests/test_runner_registry_heartbeat_starvation.py +0 -100
- hugpy_engine-0.2.6/tests/test_slot_agent_loading_state.py +0 -75
- hugpy_engine-0.2.6/tests/test_slot_load_stall_health.py +0 -228
- hugpy_engine-0.2.6/tests/test_slot_moe_vram_preflight.py +0 -169
- hugpy_engine-0.2.6/tests/test_slot_parallel_kv.py +0 -37
- hugpy_engine-0.2.6/tests/test_slot_placement_projection.py +0 -37
- hugpy_engine-0.2.6/tests/test_slot_port_guard.py +0 -50
- hugpy_engine-0.2.6/tests/test_slot_relaunch.py +0 -211
- hugpy_engine-0.2.6/tests/test_slot_served_ctx.py +0 -24
- hugpy_engine-0.2.6/tests/test_spill_ctx_reserve.py +0 -414
- hugpy_engine-0.2.6/tests/test_spill_vision_fit.py +0 -233
- hugpy_engine-0.2.6/tests/test_tasks_registry.py +0 -186
- hugpy_engine-0.2.6/tests/test_tf_child.py +0 -111
- hugpy_engine-0.2.6/tests/test_tf_slot_and_routing.py +0 -57
- hugpy_engine-0.2.6/tests/test_tok_s_plausible_ceiling.py +0 -51
- hugpy_engine-0.2.6/tests/test_transformers_kv_geometry_hybrid_vl.py +0 -30
- hugpy_engine-0.2.6/tests/test_transformers_placement.py +0 -130
- hugpy_engine-0.2.6/tests/test_video_model_classification.py +0 -289
- hugpy_engine-0.2.6/tests/test_vision_needs_slot.py +0 -242
- hugpy_engine-0.2.6/tests/test_vram_cliff.py +0 -766
- hugpy_engine-0.2.6/tests/test_worker_error_attribution.py +0 -66
- hugpy_engine-0.2.6/tests/test_worker_serve_path_central_only.py +0 -104
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/setup.cfg +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/categories.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/apis → hugpy_engine-0.3.1/src/hugpy_engine/config}/__init__.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/config → hugpy_engine-0.3.1/src/hugpy_engine/config/models}/__init__.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/config/models → hugpy_engine-0.3.1/src/hugpy_engine/fit}/__init__.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/fit/types.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/schemas → hugpy_engine-0.3.1/src/hugpy_engine/llama}/__init__.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/utils → hugpy_engine-0.3.1/src/hugpy_engine/llama/runners}/__init__.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/name_match.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/py.typed +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/resolvers/assure_model_key.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/resolvers/categories/__init__.py +0 -0
- {hugpy_engine-0.2.6/src/hugpy_engine/wire → hugpy_engine-0.3.1/src/hugpy_engine/schemas}/__init__.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/metadata_schemas.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/schemas/model_schemas.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine/tasks.py +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine.egg-info/dependency_links.txt +0 -0
- {hugpy_engine-0.2.6 → hugpy_engine-0.3.1}/src/hugpy_engine.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hugpy-engine
|
|
3
|
+
Version: 0.3.1
|
|
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
|
+
Keywords: hugpy,llm,self-hosted,inference,llama.cpp,gguf,transformers,model-serving
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: hugpy-platform
|
|
17
|
+
Requires-Dist: abstract-essentials
|
|
18
|
+
Requires-Dist: pydantic>=2
|
|
19
|
+
|
|
20
|
+
# hugpy-engine
|
|
21
|
+
|
|
22
|
+
lean 0.3.0 — shared helpers for hugpy arms; everything else retired.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hugpy-engine"
|
|
7
|
+
version = "0.3.1"
|
|
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
|
+
authors = [{ name = "putkoff", email = "support@hugpy.ai" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"hugpy",
|
|
15
|
+
"llm",
|
|
16
|
+
"self-hosted",
|
|
17
|
+
"inference",
|
|
18
|
+
"llama.cpp",
|
|
19
|
+
"gguf",
|
|
20
|
+
"transformers",
|
|
21
|
+
"model-serving",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"hugpy-platform",
|
|
33
|
+
"abstract-essentials",
|
|
34
|
+
"pydantic>=2",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
hugpy_engine = ["py.typed"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Hugpy inference engine — the one-call prompt API, revived.
|
|
2
|
+
|
|
3
|
+
``execute_prompt(*args, **kwargs)`` returns the output you expected for a task:
|
|
4
|
+
positional args are inferred by type (str -> a model id or a prompt, int ->
|
|
5
|
+
max_new_tokens, float -> temperature then top_p, bool -> do_sample, list ->
|
|
6
|
+
messages), the request is resolved to a model + task, and the call is handed to
|
|
7
|
+
the box's fitevict front door through hugpy_core's relay (which picks the
|
|
8
|
+
worker and owns evict-to-fit, load, serving and the call log).
|
|
9
|
+
|
|
10
|
+
Chat/vision run today; a task whose runner has no front-door endpoint yet
|
|
11
|
+
resolves to a clean, named error (fail-as-data).
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from importlib.metadata import version as _package_version
|
|
17
|
+
|
|
18
|
+
__version__ = _package_version("hugpy-engine")
|
|
19
|
+
except Exception: # pragma: no cover - source tree without metadata
|
|
20
|
+
__version__ = "0.0.0+unknown"
|
|
21
|
+
|
|
22
|
+
from hugpy_engine.dispatch.dispatch import (
|
|
23
|
+
execute_chat_stream,
|
|
24
|
+
execute_prompt,
|
|
25
|
+
execute_prompt_stream,
|
|
26
|
+
infer_arg_name,
|
|
27
|
+
normalize_prompt_kwargs,
|
|
28
|
+
runner_for,
|
|
29
|
+
)
|
|
30
|
+
from hugpy_engine.resolvers import resolve, resolve_model_key
|
|
31
|
+
from hugpy_engine.config.models.models_config import (
|
|
32
|
+
MODEL_REGISTRY,
|
|
33
|
+
get_model_registry,
|
|
34
|
+
refresh_registry,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"MODEL_REGISTRY",
|
|
39
|
+
"execute_chat_stream",
|
|
40
|
+
"execute_prompt",
|
|
41
|
+
"execute_prompt_stream",
|
|
42
|
+
"get_model_registry",
|
|
43
|
+
"infer_arg_name",
|
|
44
|
+
"normalize_prompt_kwargs",
|
|
45
|
+
"refresh_registry",
|
|
46
|
+
"resolve",
|
|
47
|
+
"resolve_model_key",
|
|
48
|
+
"runner_for",
|
|
49
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Registry lookups over the live MODEL_REGISTRY (hugpy_next cut 2, lean).
|
|
2
|
+
|
|
3
|
+
The heavy disk/path resolution of the legacy config.main is DROPPED — hugpy
|
|
4
|
+
hosts all weights and the box's fitevict front door owns every load, so the
|
|
5
|
+
engine never resolves a model directory. These helpers answer "what are the
|
|
6
|
+
facts of model X" from the registry (sourced from hugpy_core's catalog).
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from hugpy_engine.config.models.models_config import (
|
|
13
|
+
MODEL_REGISTRY,
|
|
14
|
+
get_model_registry,
|
|
15
|
+
refresh_registry,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def list_models():
|
|
20
|
+
refresh_registry()
|
|
21
|
+
return list(MODEL_REGISTRY.keys())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _resolve_model_key(model_key, registry, prefer=None):
|
|
25
|
+
"""Map a possibly-bare model_key to a concrete registry key (exact, then a
|
|
26
|
+
unique ``owner~name`` tail, then the ``-GGUF`` format equivalent)."""
|
|
27
|
+
if model_key in registry:
|
|
28
|
+
return model_key
|
|
29
|
+
|
|
30
|
+
def _bare(k):
|
|
31
|
+
return k.split("~", 1)[1] if "~" in k else k
|
|
32
|
+
|
|
33
|
+
candidates = sorted(k for k in registry if _bare(k) == model_key)
|
|
34
|
+
if not candidates:
|
|
35
|
+
try:
|
|
36
|
+
from hugpy_engine.fit.types import key_equivalent
|
|
37
|
+
candidates = sorted(k for k in registry if key_equivalent(_bare(k), model_key))
|
|
38
|
+
except Exception: # noqa: BLE001
|
|
39
|
+
candidates = []
|
|
40
|
+
if not candidates:
|
|
41
|
+
return None
|
|
42
|
+
if len(candidates) == 1:
|
|
43
|
+
return candidates[0]
|
|
44
|
+
for p in (prefer or []):
|
|
45
|
+
if p in candidates:
|
|
46
|
+
return p
|
|
47
|
+
for c in candidates:
|
|
48
|
+
if _bare(c) == _bare(p):
|
|
49
|
+
return c
|
|
50
|
+
return candidates[0]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_model_config(model_key: str = None, dict_return=False, return_dict=False,
|
|
54
|
+
key: str = None, prefer=None):
|
|
55
|
+
model_key = model_key or key
|
|
56
|
+
refresh_registry()
|
|
57
|
+
registry = get_model_registry(dict_return=dict_return, return_dict=return_dict)
|
|
58
|
+
resolved = _resolve_model_key(model_key, MODEL_REGISTRY, prefer=prefer)
|
|
59
|
+
if resolved is None:
|
|
60
|
+
raise KeyError(f"Unknown model: {model_key}")
|
|
61
|
+
return registry[resolved]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_model_path(key: str) -> Optional[str]:
|
|
65
|
+
"""The model's folder as recorded on its registry row. Not load-bearing (the
|
|
66
|
+
front door routes by model_key); best-effort for callers that still ask."""
|
|
67
|
+
cfg = get_model_config(key)
|
|
68
|
+
return getattr(cfg, "folder", None)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def list_model_options():
|
|
72
|
+
refresh_registry()
|
|
73
|
+
return {
|
|
74
|
+
key: {
|
|
75
|
+
"name": cfg.name,
|
|
76
|
+
"hub_id": cfg.hub_id,
|
|
77
|
+
"folder": cfg.folder,
|
|
78
|
+
"tasks": cfg.tasks,
|
|
79
|
+
"framework": cfg.framework,
|
|
80
|
+
"filename": cfg.filename,
|
|
81
|
+
"port": cfg.port,
|
|
82
|
+
}
|
|
83
|
+
for key, cfg in MODEL_REGISTRY.items()
|
|
84
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""models_config.py — the registry SOURCE (hugpy_next cut 2 seam).
|
|
2
|
+
|
|
3
|
+
Operator ruling "hugpy.json is the model init": per-model facts (framework,
|
|
4
|
+
tasks, primary_task, hub_id, name) are stamped in each model's hugpy.json. The
|
|
5
|
+
worker stamps them, derives the model KEY per box (``owner~repo`` on an owner
|
|
6
|
+
collision, else bare ``repo`` — fitevict/adapters/llama_cpp.discover_models),
|
|
7
|
+
and relays both the key and the parsed hugpy.json to hugpy_core, which exposes
|
|
8
|
+
them at ``GET {core}/models`` (catalog_rows). That endpoint is the ONE place
|
|
9
|
+
that carries BOTH the core id (the id ``/v1/models`` serves and ``pick()``
|
|
10
|
+
routes on) AND each model's hugpy.json facts — the core id is not derivable from
|
|
11
|
+
a single hugpy.json on disk (the collision qualifier is per-box state), so the
|
|
12
|
+
registry is keyed from core, never re-derived from disk.
|
|
13
|
+
|
|
14
|
+
So: MODEL_REGISTRY is built by reading hugpy_core's catalog, keyed by the core
|
|
15
|
+
id, mapping each row's hugpy.json facts through ``derive_model_config_row`` into
|
|
16
|
+
``ModelConfig``. Read lazily, TTL-cached, fail-open: core unreachable / no
|
|
17
|
+
workers online -> the registry keeps whatever it last had (empty at worst), and
|
|
18
|
+
resolution then refuses cleanly ("no online worker has it" / "no chat default").
|
|
19
|
+
|
|
20
|
+
There is NO curated ``MODELS`` fleet and NO disk discovery/classification here
|
|
21
|
+
(operator: no hardcoded model names; every model already carries its task).
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import threading
|
|
29
|
+
import time
|
|
30
|
+
import urllib.request
|
|
31
|
+
from typing import Dict, Optional, Tuple
|
|
32
|
+
|
|
33
|
+
from hugpy_engine.schemas.model_schemas import ModelConfig
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
# No curated staples (operator: no hardcoded model names as defaults). Kept as an
|
|
38
|
+
# empty dict because a few lifted call sites still read ``MODELS`` (e.g.
|
|
39
|
+
# validate_registry's "is this a code-declared staple" check, media defaults).
|
|
40
|
+
MODELS: Dict[str, dict] = {}
|
|
41
|
+
|
|
42
|
+
# The live registry, keyed by the core id. Updated IN PLACE by refresh_registry
|
|
43
|
+
# so modules that imported these names by reference always see the current set.
|
|
44
|
+
MODEL_REGISTRY: Dict[str, ModelConfig] = {}
|
|
45
|
+
MODEL_REGISTRY_DICT: Dict[str, dict] = {}
|
|
46
|
+
|
|
47
|
+
_CORE_URL = (os.environ.get("HUGPY_CORE_URL") or "http://127.0.0.1:7020").rstrip("/")
|
|
48
|
+
_TTL_S = float(os.environ.get("HUGPY_REGISTRY_TTL_S", "30"))
|
|
49
|
+
_TIMEOUT_S = float(os.environ.get("HUGPY_REGISTRY_TIMEOUT_S", "10"))
|
|
50
|
+
_lock = threading.RLock()
|
|
51
|
+
_last_fetch = 0.0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _dedup(seq) -> list:
|
|
55
|
+
"""Order-preserving de-dup, dropping falsy entries."""
|
|
56
|
+
out: list = []
|
|
57
|
+
for x in seq or []:
|
|
58
|
+
if x and x not in out:
|
|
59
|
+
out.append(x)
|
|
60
|
+
return out
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def derive_model_config_row(name, row) -> "Tuple[Optional[dict], Optional[str]]":
|
|
64
|
+
"""One hugpy_core catalog row -> a ModelConfig-ready dict, or (None, reason).
|
|
65
|
+
|
|
66
|
+
``name`` is the core id (the registry key). ``row`` is a catalog_rows entry;
|
|
67
|
+
its ``hugpy_json`` sub-dict is authoritative for framework/tasks/
|
|
68
|
+
primary_task/hub_id/name (operator: hugpy.json is the model init), with the
|
|
69
|
+
row's own top-level fields as the fallback the catalog already flattened.
|
|
70
|
+
|
|
71
|
+
framework == the wrapper's engine (gguf, transformers, comfy, ...): one
|
|
72
|
+
concept, kept verbatim. tasks are the plain HF pipeline-tag strings straight
|
|
73
|
+
from hugpy.json, no renaming. A null ``tasks`` (seen on some GGUF markers)
|
|
74
|
+
falls back to ``[primary_task]`` so every model has at least its own task.
|
|
75
|
+
"""
|
|
76
|
+
hj = row.get("hugpy_json") if isinstance(row.get("hugpy_json"), dict) else {}
|
|
77
|
+
|
|
78
|
+
framework = hj.get("framework") or row.get("framework")
|
|
79
|
+
if isinstance(framework, (list, tuple)):
|
|
80
|
+
framework = framework[0] if framework else None
|
|
81
|
+
framework = str(framework or "").strip().lower() or None
|
|
82
|
+
if not framework:
|
|
83
|
+
return None, f"{name}: no framework/engine recorded"
|
|
84
|
+
|
|
85
|
+
tasks = hj.get("tasks") if isinstance(hj.get("tasks"), list) else None
|
|
86
|
+
if not tasks:
|
|
87
|
+
tasks = row.get("tasks") if isinstance(row.get("tasks"), list) else None
|
|
88
|
+
primary = hj.get("primary_task") or row.get("primary_task") or row.get("task")
|
|
89
|
+
tasks = _dedup(tasks or ([primary] if primary else []))
|
|
90
|
+
if not tasks:
|
|
91
|
+
return None, f"{name}: no tasks and no primary_task recorded"
|
|
92
|
+
if not primary or primary not in tasks:
|
|
93
|
+
primary = tasks[0]
|
|
94
|
+
else:
|
|
95
|
+
# primary must be present; already is. Keep order with primary first is
|
|
96
|
+
# not required (ModelConfig only checks membership).
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
hub_id = hj.get("hub_id") or row.get("hub_id") or name
|
|
100
|
+
disp_name = hj.get("name") or row.get("name") or name
|
|
101
|
+
# folder is a required ModelConfig field; it is not load-bearing here (the
|
|
102
|
+
# fitevict front door routes by model_key, not folder), so the hub_id is a
|
|
103
|
+
# fine stable value.
|
|
104
|
+
folder = str(hub_id or name)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
"name": disp_name,
|
|
108
|
+
"model_key": name,
|
|
109
|
+
"hub_id": str(hub_id),
|
|
110
|
+
"folder": folder,
|
|
111
|
+
"framework": framework,
|
|
112
|
+
"tasks": tasks,
|
|
113
|
+
"primary_task": primary,
|
|
114
|
+
"filename": hj.get("filename") or row.get("file"),
|
|
115
|
+
"include": hj.get("include"),
|
|
116
|
+
"model_max_length": hj.get("model_max_length") or row.get("model_max_length"),
|
|
117
|
+
}, None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _fetch_catalog() -> "Optional[list]":
|
|
121
|
+
"""GET {core}/models (catalog_rows). None on any failure (fail-open)."""
|
|
122
|
+
url = _CORE_URL + "/models"
|
|
123
|
+
try:
|
|
124
|
+
req = urllib.request.Request(url, headers={"Accept": "application/json"})
|
|
125
|
+
with urllib.request.urlopen(req, timeout=_TIMEOUT_S) as r:
|
|
126
|
+
data = json.loads(r.read().decode("utf-8") or "null")
|
|
127
|
+
except Exception as exc: # noqa: BLE001 — the registry read never raises
|
|
128
|
+
logger.warning("registry: could not read %s (%s); keeping the last "
|
|
129
|
+
"known model set (%d models)", url, exc, len(MODEL_REGISTRY))
|
|
130
|
+
return None
|
|
131
|
+
if isinstance(data, dict):
|
|
132
|
+
data = data.get("data") or data.get("models") or []
|
|
133
|
+
return data if isinstance(data, list) else []
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def refresh_registry(force: bool = False) -> None:
|
|
137
|
+
"""Rebuild MODEL_REGISTRY / MODEL_REGISTRY_DICT from hugpy_core's catalog.
|
|
138
|
+
|
|
139
|
+
TTL-cached (HUGPY_REGISTRY_TTL_S) so a burst of calls hits core once. Updates
|
|
140
|
+
the dicts IN PLACE. Fail-open: a failed/empty fetch leaves the current set
|
|
141
|
+
untouched rather than emptying a working registry."""
|
|
142
|
+
global _last_fetch
|
|
143
|
+
now = time.time()
|
|
144
|
+
with _lock:
|
|
145
|
+
if not force and MODEL_REGISTRY and (now - _last_fetch) < _TTL_S:
|
|
146
|
+
return
|
|
147
|
+
rows = _fetch_catalog()
|
|
148
|
+
_last_fetch = now
|
|
149
|
+
if rows is None:
|
|
150
|
+
return # keep the last known set
|
|
151
|
+
fresh: Dict[str, ModelConfig] = {}
|
|
152
|
+
fresh_dict: Dict[str, dict] = {}
|
|
153
|
+
dropped = []
|
|
154
|
+
for row in rows:
|
|
155
|
+
if not isinstance(row, dict):
|
|
156
|
+
continue
|
|
157
|
+
key = row.get("model_key") or row.get("id") or row.get("name")
|
|
158
|
+
if not key:
|
|
159
|
+
continue
|
|
160
|
+
values, why = derive_model_config_row(key, row)
|
|
161
|
+
if values is None:
|
|
162
|
+
dropped.append((key, why))
|
|
163
|
+
continue
|
|
164
|
+
try:
|
|
165
|
+
cfg = ModelConfig(**values)
|
|
166
|
+
except Exception as exc: # noqa: BLE001 — a bad row never breaks the set
|
|
167
|
+
dropped.append((key, f"ModelConfig rejected: {exc}"))
|
|
168
|
+
continue
|
|
169
|
+
fresh[key] = cfg
|
|
170
|
+
fresh_dict[key] = cfg.to_dict()
|
|
171
|
+
if not fresh and MODEL_REGISTRY:
|
|
172
|
+
# Core answered but yielded nothing usable (all workers offline mid
|
|
173
|
+
# refresh): keep the last good set rather than blanking resolution.
|
|
174
|
+
logger.info("registry: catalog refresh yielded 0 usable rows; "
|
|
175
|
+
"keeping %d previous models", len(MODEL_REGISTRY))
|
|
176
|
+
return
|
|
177
|
+
MODEL_REGISTRY.clear(); MODEL_REGISTRY.update(fresh)
|
|
178
|
+
MODEL_REGISTRY_DICT.clear(); MODEL_REGISTRY_DICT.update(fresh_dict)
|
|
179
|
+
if dropped:
|
|
180
|
+
logger.info("registry: %d/%d catalog rows not servable here: %s",
|
|
181
|
+
len(dropped), len(rows),
|
|
182
|
+
", ".join(f"{k} ({w})" for k, w in dropped[:6]))
|
|
183
|
+
logger.info("registry: refreshed from %s — %d models",
|
|
184
|
+
_CORE_URL, len(MODEL_REGISTRY))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_model_registry(dict_return: bool = False, return_dict: bool = False):
|
|
188
|
+
if dict_return or return_dict:
|
|
189
|
+
return MODEL_REGISTRY_DICT
|
|
190
|
+
return MODEL_REGISTRY
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Default-model reconciliation (hugpy_next cut 2, lean).
|
|
2
|
+
|
|
3
|
+
TASK_DEFAULTS / MEDIA_DEFAULTS are seeded in categories.py from the configured
|
|
4
|
+
``DEFAULT_*`` constants. Those seeds are names a fresh box may not actually
|
|
5
|
+
serve, so they are RECONCILED against the live registry (sourced from
|
|
6
|
+
hugpy_core's catalog, keyed by core id): keep the configured default when the
|
|
7
|
+
registry holds it, else stand in a registry model that serves the task group.
|
|
8
|
+
|
|
9
|
+
Every registry row here is a model a worker is serving RIGHT NOW (it came from
|
|
10
|
+
the live catalog), so "installed" is implicit — there is no disk check and no
|
|
11
|
+
``route_destination`` import. ``reconcile_defaults()`` and
|
|
12
|
+
``refresh_task_registries()`` are re-callable so resolve() heals the dicts from
|
|
13
|
+
the current model set on every request.
|
|
14
|
+
"""
|
|
15
|
+
from typing import Dict
|
|
16
|
+
|
|
17
|
+
from abstract_essentials import make_list
|
|
18
|
+
from hugpy_engine.categories import MEDIA_DEFAULTS, TASK_DEFAULTS
|
|
19
|
+
from hugpy_engine.config.models.models_config import MODEL_REGISTRY
|
|
20
|
+
from hugpy_engine.schemas.model_schemas import ModelConfig
|
|
21
|
+
from hugpy_platform.constants import (
|
|
22
|
+
DEFAULT_AGENT_BRAIN,
|
|
23
|
+
DEFAULT_CHAT_MODEL,
|
|
24
|
+
DEFAULT_EMBED_MODEL,
|
|
25
|
+
DEFAULT_VISION_MODEL,
|
|
26
|
+
DEFAULT_WHISPER_MODEL,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_context_tokens():
|
|
31
|
+
return {k: v.model_max_length for k, v in MODEL_REGISTRY.items()}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def default_context_tokens_for_model(model_key: str) -> int:
|
|
35
|
+
return get_context_tokens().get(model_key, 8192)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_models_dict_by_tasks(tasks=None):
|
|
39
|
+
tasks = make_list(tasks or [])
|
|
40
|
+
return {k: v for k, v in MODEL_REGISTRY.items()
|
|
41
|
+
if any(t in tasks for t in v.tasks)}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_models_dict_by_names(names=None):
|
|
45
|
+
names = make_list(names or [])
|
|
46
|
+
out = {}
|
|
47
|
+
for k, v in MODEL_REGISTRY.items():
|
|
48
|
+
for name in names:
|
|
49
|
+
if name in v.name:
|
|
50
|
+
out[k] = v
|
|
51
|
+
break
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _reconcile_default(configured: str, registry: Dict[str, ModelConfig],
|
|
56
|
+
prefer_task: str | None = None) -> str:
|
|
57
|
+
"""Keep ``configured`` when the registry holds it; otherwise stand in a
|
|
58
|
+
registry model that serves ``prefer_task`` (then key order for determinism).
|
|
59
|
+
Returns ``configured`` unchanged when the registry is empty, so the honest
|
|
60
|
+
"not in MODEL_REGISTRY" refusal downstream stays accurate."""
|
|
61
|
+
if not registry:
|
|
62
|
+
return configured
|
|
63
|
+
if configured in registry:
|
|
64
|
+
return configured
|
|
65
|
+
pool = registry
|
|
66
|
+
if prefer_task:
|
|
67
|
+
preferred = {k: v for k, v in registry.items() if prefer_task in v.tasks}
|
|
68
|
+
pool = preferred or registry
|
|
69
|
+
if not pool:
|
|
70
|
+
return configured
|
|
71
|
+
return min(pool)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Seeds from constants.py; healed by reconcile_defaults() against the live set.
|
|
75
|
+
_SEED = {
|
|
76
|
+
"chat": DEFAULT_CHAT_MODEL,
|
|
77
|
+
"vision": DEFAULT_VISION_MODEL,
|
|
78
|
+
"whisper": DEFAULT_WHISPER_MODEL,
|
|
79
|
+
"embed": DEFAULT_EMBED_MODEL,
|
|
80
|
+
"brain": DEFAULT_AGENT_BRAIN,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Task-filtered views (updated IN PLACE — other modules hold them by reference).
|
|
84
|
+
CHAT_MODELS_REGISTRY: Dict[str, ModelConfig] = {}
|
|
85
|
+
VISION_MODELS_REGISTRY: Dict[str, ModelConfig] = {}
|
|
86
|
+
WHISPER_MODELS_REGISTRY: Dict[str, ModelConfig] = {}
|
|
87
|
+
EMBED_MODELS_REGISTRY: Dict[str, ModelConfig] = {}
|
|
88
|
+
|
|
89
|
+
_TASK_REGISTRY_GROUPS = (
|
|
90
|
+
(CHAT_MODELS_REGISTRY, ["text-generation", "text-generation-inference", "text2text-generation"]),
|
|
91
|
+
(VISION_MODELS_REGISTRY, ["image-text-to-text", "text-to-image"]),
|
|
92
|
+
(WHISPER_MODELS_REGISTRY, ["automatic-speech-recognition", "speech-recognition"]),
|
|
93
|
+
(EMBED_MODELS_REGISTRY, ["feature-extraction", "sentence-similarity", "sentence-transformers"]),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def refresh_task_registries() -> None:
|
|
98
|
+
"""Rebuild the task-filtered registries in place from the live MODEL_REGISTRY
|
|
99
|
+
(update-then-prune so a concurrent reader never sees an empty dict)."""
|
|
100
|
+
for registry, tasks in _TASK_REGISTRY_GROUPS:
|
|
101
|
+
fresh = get_models_dict_by_tasks(tasks=tasks)
|
|
102
|
+
registry.update(fresh)
|
|
103
|
+
for stale in [k for k in registry if k not in fresh]:
|
|
104
|
+
registry.pop(stale, None)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# The reconciled defaults (module globals, rebound by reconcile_defaults()).
|
|
108
|
+
DEFAULT_CHAT_MODEL = _SEED["chat"]
|
|
109
|
+
DEFAULT_VISION_MODEL = _SEED["vision"]
|
|
110
|
+
DEFAULT_WHISPER_MODEL = _SEED["whisper"]
|
|
111
|
+
DEFAULT_EMBED_MODEL = _SEED["embed"]
|
|
112
|
+
DEFAULT_AGENT_BRAIN = _SEED["brain"]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def reconcile_defaults() -> None:
|
|
116
|
+
"""Recompute the DEFAULT_* model keys from the live registry and HEAL
|
|
117
|
+
TASK_DEFAULTS / MEDIA_DEFAULTS in place so a task-only / media-only / bare
|
|
118
|
+
request resolves to a real core id instead of a dangling configured name."""
|
|
119
|
+
global DEFAULT_CHAT_MODEL, DEFAULT_VISION_MODEL, DEFAULT_WHISPER_MODEL
|
|
120
|
+
global DEFAULT_EMBED_MODEL, DEFAULT_AGENT_BRAIN
|
|
121
|
+
refresh_task_registries()
|
|
122
|
+
DEFAULT_CHAT_MODEL = _reconcile_default(_SEED["chat"], CHAT_MODELS_REGISTRY,
|
|
123
|
+
prefer_task="text-generation")
|
|
124
|
+
DEFAULT_AGENT_BRAIN = _reconcile_default(_SEED["brain"], CHAT_MODELS_REGISTRY,
|
|
125
|
+
prefer_task="text-generation")
|
|
126
|
+
DEFAULT_VISION_MODEL = _reconcile_default(_SEED["vision"], VISION_MODELS_REGISTRY,
|
|
127
|
+
prefer_task="image-text-to-text")
|
|
128
|
+
DEFAULT_WHISPER_MODEL = _reconcile_default(_SEED["whisper"], WHISPER_MODELS_REGISTRY,
|
|
129
|
+
prefer_task="automatic-speech-recognition")
|
|
130
|
+
DEFAULT_EMBED_MODEL = _reconcile_default(_SEED["embed"], EMBED_MODELS_REGISTRY,
|
|
131
|
+
prefer_task="feature-extraction")
|
|
132
|
+
reconciled = {
|
|
133
|
+
_SEED["chat"]: DEFAULT_CHAT_MODEL,
|
|
134
|
+
_SEED["vision"]: DEFAULT_VISION_MODEL,
|
|
135
|
+
_SEED["whisper"]: DEFAULT_WHISPER_MODEL,
|
|
136
|
+
_SEED["embed"]: DEFAULT_EMBED_MODEL,
|
|
137
|
+
}
|
|
138
|
+
for defaults, keys_are_tasks in ((TASK_DEFAULTS, True), (MEDIA_DEFAULTS, False)):
|
|
139
|
+
for key, model in list(defaults.items()):
|
|
140
|
+
replacement = reconciled.get(model)
|
|
141
|
+
if not replacement or replacement == model:
|
|
142
|
+
continue
|
|
143
|
+
if keys_are_tasks and replacement in MODEL_REGISTRY \
|
|
144
|
+
and key not in MODEL_REGISTRY[replacement].tasks:
|
|
145
|
+
continue # the stand-in serves the group but not this exact task
|
|
146
|
+
defaults[key] = replacement
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# Heal once at import (registry may be empty here -> seeds, re-healed per request).
|
|
150
|
+
reconcile_defaults()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"""Request dispatch: runner cache, execution, streaming, eviction."""
|
|
2
|
-
from hugpy_engine.dispatch.acquire import acquire
|
|
3
2
|
from hugpy_engine.dispatch.dispatch import (
|
|
4
3
|
LoadRefusal,
|
|
5
4
|
clear,
|
|
@@ -27,7 +26,7 @@ from hugpy_engine.dispatch.dispatch import (
|
|
|
27
26
|
)
|
|
28
27
|
|
|
29
28
|
__all__ = [
|
|
30
|
-
"LoadRefusal", "
|
|
29
|
+
"LoadRefusal", "clear", "ensure_headroom_for_load", "evict",
|
|
31
30
|
"execute_chat_stream", "execute_prompt", "execute_prompt_stream",
|
|
32
31
|
"infer_arg_name", "last_used_snapshot", "loaded_disk_detail",
|
|
33
32
|
"loaded_model_keys", "loading_model_keys", "no_makeroom_active",
|
|
@@ -562,11 +562,31 @@ def infer_arg_name(arg: Any) -> Optional[str]:
|
|
|
562
562
|
"/" in arg
|
|
563
563
|
or "_gguf" in lowered
|
|
564
564
|
or any(tag in lowered for tag in ("qwen", "llama", "mistral", "gpt"))
|
|
565
|
+
# Any live registry key is a model_key — core ids like
|
|
566
|
+
# ``AtomicChat~Qwen3.8-27B-GGUF`` / ``Echo-Mini`` carry none of the
|
|
567
|
+
# name tags above, so the positional model id must be recognised by
|
|
568
|
+
# membership (the one allowed addition, operator 2026-10-04). Guarded
|
|
569
|
+
# + fail-open: an unreachable/empty registry just leaves it "messages".
|
|
570
|
+
or _is_registry_key(arg)
|
|
565
571
|
)
|
|
566
572
|
return "model_key" if looks_like_model else "messages"
|
|
567
573
|
return None
|
|
568
574
|
|
|
569
575
|
|
|
576
|
+
def _is_registry_key(arg: str) -> bool:
|
|
577
|
+
"""True when ``arg`` names a model the registry holds (exact or resolvable).
|
|
578
|
+
Fail-open to False so a registry/import hiccup never misclassifies a prompt."""
|
|
579
|
+
try:
|
|
580
|
+
from hugpy_engine.config.models.models_config import MODEL_REGISTRY, refresh_registry
|
|
581
|
+
refresh_registry()
|
|
582
|
+
if arg in MODEL_REGISTRY:
|
|
583
|
+
return True
|
|
584
|
+
from hugpy_engine.resolvers.assure_model_key import assure_model_key
|
|
585
|
+
return assure_model_key(arg, fuzzy=False) is not None
|
|
586
|
+
except Exception: # noqa: BLE001
|
|
587
|
+
return False
|
|
588
|
+
|
|
589
|
+
|
|
570
590
|
def normalize_prompt_kwargs(*args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
571
591
|
"""Convert flexible input into builder-compatible kwargs.
|
|
572
592
|
|
|
@@ -612,6 +632,20 @@ def runner_for(
|
|
|
612
632
|
return _get_or_build_runner(res)
|
|
613
633
|
|
|
614
634
|
|
|
635
|
+
def _run_coro_sync(coro):
|
|
636
|
+
"""Drive an awaitable to its result from a SYNC caller. Uses asyncio.run when
|
|
637
|
+
no loop is running; if one is (rare for this entrypoint), runs it on a short
|
|
638
|
+
worker thread so execute_prompt still returns a concrete result rather than a
|
|
639
|
+
coroutine the sync caller would drop on the floor."""
|
|
640
|
+
try:
|
|
641
|
+
asyncio.get_running_loop()
|
|
642
|
+
except RuntimeError:
|
|
643
|
+
return asyncio.run(coro)
|
|
644
|
+
import concurrent.futures
|
|
645
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as ex:
|
|
646
|
+
return ex.submit(lambda: asyncio.run(coro)).result()
|
|
647
|
+
|
|
648
|
+
|
|
615
649
|
def execute_prompt(*args: Any, **kwargs: Any):
|
|
616
650
|
"""One-shot request -> result. Sync entrypoint; awaits inside if needed.
|
|
617
651
|
|
|
@@ -625,7 +659,15 @@ def execute_prompt(*args: Any, **kwargs: Any):
|
|
|
625
659
|
req = res.builder(prompt_kwargs, res.model_key)
|
|
626
660
|
runner = _get_or_build_runner(res)
|
|
627
661
|
touch_model(res.model_key) # residency idle clock
|
|
628
|
-
|
|
662
|
+
out = runner.run(req=req)
|
|
663
|
+
# Sync one-call contract (the shape the operator loves): a sync caller
|
|
664
|
+
# gets the RESULT, not a coroutine. Runners are async now (the fitevict
|
|
665
|
+
# front door), so await-to-completion here when the caller isn't already
|
|
666
|
+
# in an event loop; under a running loop, hand back the awaitable so the
|
|
667
|
+
# async caller (the HTTP route via await_sync) drives it.
|
|
668
|
+
if inspect.isawaitable(out):
|
|
669
|
+
return _run_coro_sync(out)
|
|
670
|
+
return out
|
|
629
671
|
finally:
|
|
630
672
|
# The try body can resume in a DIFFERENT Context (async/greenlet hop),
|
|
631
673
|
# where reset(token) raises "created in a different Context" and 500s
|