hugpy-resources 0.1.3__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 (137) hide show
  1. hugpy_resources-0.1.3/PKG-INFO +10 -0
  2. hugpy_resources-0.1.3/README.md +144 -0
  3. hugpy_resources-0.1.3/pyproject.toml +20 -0
  4. hugpy_resources-0.1.3/setup.cfg +4 -0
  5. hugpy_resources-0.1.3/src/hugpy_resources/__init__.py +3 -0
  6. hugpy_resources-0.1.3/src/hugpy_resources/imports/__init__.py +35 -0
  7. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/__init__.py +24 -0
  8. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/database.py +49 -0
  9. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/errors.py +74 -0
  10. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/http.py +116 -0
  11. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/nvml.py +32 -0
  12. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/thinking.py +77 -0
  13. hugpy_resources-0.1.3/src/hugpy_resources/imports/classes/tool_calls.py +53 -0
  14. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/__init__.py +45 -0
  15. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/admission.py +19 -0
  16. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/architecture.py +84 -0
  17. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/audit.py +6 -0
  18. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/comfy.py +15 -0
  19. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/context.py +37 -0
  20. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/contracts.py +279 -0
  21. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/cpu.py +19 -0
  22. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/drives.py +26 -0
  23. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/engines.py +15 -0
  24. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/formats.py +114 -0
  25. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/gguf.py +88 -0
  26. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/hardware.py +21 -0
  27. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/identity.py +16 -0
  28. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/math.py +38 -0
  29. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/memory.py +31 -0
  30. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/placement.py +140 -0
  31. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/platform.py +29 -0
  32. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/runtime.py +175 -0
  33. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/tensors.py +53 -0
  34. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/terms.py +221 -0
  35. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/text.py +84 -0
  36. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/timing.py +39 -0
  37. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/tool_calls.py +22 -0
  38. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/transfer.py +28 -0
  39. hugpy_resources-0.1.3/src/hugpy_resources/imports/constants/validation.py +52 -0
  40. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/__init__.py +157 -0
  41. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/application_paths.py +185 -0
  42. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/argument_parsing.py +27 -0
  43. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/ast_utils.py +26 -0
  44. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/atomic_files.py +25 -0
  45. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/binary_data.py +39 -0
  46. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/block_io.py +78 -0
  47. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/cache.py +92 -0
  48. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/callables.py +10 -0
  49. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/coercion.py +22 -0
  50. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/comfy_metadata.py +101 -0
  51. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/configuration.py +29 -0
  52. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/content_proofs.py +57 -0
  53. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/cpu_activity.py +31 -0
  54. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/devices.py +10 -0
  55. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/engine_metadata.py +43 -0
  56. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/engine_probes.py +217 -0
  57. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/env_files.py +19 -0
  58. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/env_values.py +70 -0
  59. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/environment.py +82 -0
  60. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/evidence.py +37 -0
  61. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/executables.py +51 -0
  62. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/feasibility.py +130 -0
  63. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/file_transfer.py +94 -0
  64. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/file_types.py +256 -0
  65. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/filesystem.py +275 -0
  66. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/filesystems.py +30 -0
  67. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/formatting.py +55 -0
  68. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/geometry.py +217 -0
  69. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/gguf_cache.py +64 -0
  70. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/gguf_headers.py +323 -0
  71. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/gguf_moe.py +356 -0
  72. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/gguf_structure.py +288 -0
  73. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/gguf_variants.py +124 -0
  74. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/hardware_inventory.py +238 -0
  75. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/hashing.py +38 -0
  76. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/host_memory.py +121 -0
  77. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/http_auth.py +36 -0
  78. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/inference_children.py +108 -0
  79. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/inference_timings.py +182 -0
  80. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/integers.py +14 -0
  81. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/json_http.py +22 -0
  82. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/json_rows.py +75 -0
  83. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/json_utils.py +52 -0
  84. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/kv_cache.py +45 -0
  85. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/kv_cells.py +22 -0
  86. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/locking.py +21 -0
  87. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/memory.py +23 -0
  88. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/messages.py +38 -0
  89. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/model_config.py +58 -0
  90. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/model_files.py +94 -0
  91. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/nvml.py +192 -0
  92. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/openai_protocol.py +41 -0
  93. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/paths.py +245 -0
  94. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/placement.py +32 -0
  95. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/ports.py +21 -0
  96. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/processes.py +132 -0
  97. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/profile_metadata.py +30 -0
  98. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/python_environments.py +26 -0
  99. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/quantization.py +50 -0
  100. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/recurrent_state.py +33 -0
  101. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/requirements.py +204 -0
  102. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/row_history.py +77 -0
  103. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/row_values.py +29 -0
  104. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/runtime.py +236 -0
  105. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/safetensors.py +127 -0
  106. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/search.py +884 -0
  107. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/selected_artifacts.py +345 -0
  108. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/source_fingerprints.py +26 -0
  109. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/sql_quoting.py +11 -0
  110. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/sqlite.py +26 -0
  111. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/sqlite_callbacks.py +75 -0
  112. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/sqlite_connections.py +89 -0
  113. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/state.py +41 -0
  114. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/statistics.py +18 -0
  115. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/structured_data.py +72 -0
  116. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/tensor_files.py +193 -0
  117. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/tensors.py +25 -0
  118. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/terminal_text.py +82 -0
  119. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/text.py +104 -0
  120. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/thinking.py +45 -0
  121. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/time_utils.py +37 -0
  122. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/timing.py +415 -0
  123. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/tool_calls.py +86 -0
  124. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/tool_deltas.py +25 -0
  125. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/transformer_facts.py +336 -0
  126. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/units.py +25 -0
  127. hugpy_resources-0.1.3/src/hugpy_resources/imports/functions/urls.py +23 -0
  128. hugpy_resources-0.1.3/src/hugpy_resources/imports/init_imports.py +80 -0
  129. hugpy_resources-0.1.3/src/hugpy_resources/imports/mime_types.json +263 -0
  130. hugpy_resources-0.1.3/src/hugpy_resources/imports/terms.json +3841 -0
  131. hugpy_resources-0.1.3/src/hugpy_resources/lazy_imports.py +53 -0
  132. hugpy_resources-0.1.3/src/hugpy_resources.egg-info/PKG-INFO +10 -0
  133. hugpy_resources-0.1.3/src/hugpy_resources.egg-info/SOURCES.txt +135 -0
  134. hugpy_resources-0.1.3/src/hugpy_resources.egg-info/dependency_links.txt +1 -0
  135. hugpy_resources-0.1.3/src/hugpy_resources.egg-info/requires.txt +7 -0
  136. hugpy_resources-0.1.3/src/hugpy_resources.egg-info/top_level.txt +1 -0
  137. hugpy_resources-0.1.3/tests/test_deterministic_tiers.py +135 -0
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: hugpy-resources
3
+ Version: 0.1.3
4
+ Summary: Shared generic utilities, file inspection and model resource evidence contracts
5
+ Requires-Python: >=3.9
6
+ Provides-Extra: inspection
7
+ Requires-Dist: sentencepiece>=0.2; extra == "inspection"
8
+ Requires-Dist: protobuf>=4; extra == "inspection"
9
+ Provides-Extra: yaml
10
+ Requires-Dist: PyYAML; extra == "yaml"
@@ -0,0 +1,144 @@
1
+ # Shared resource evidence
2
+
3
+ `hugpy-resources` owns pure payload formulas, Transformers config geometry and
4
+ resource-term accounting for Discovery and Wrapper. Its base install uses the standard library; it imports no engine, external
5
+ database driver or hardware probe. Shared file inspection and filesystem
6
+ utilities perform their work only when called. Consumer import boundaries resolve the canonical definitions; formula catalog pointers name their actual owners.
7
+
8
+ `imports.functions.requirements.transformer_memory` returns per-device loading and serving terms.
9
+ Their maximum is the required peak; any missing term leaves that peak unknown.
10
+ Historical measurements are retained as component observations. They do not
11
+ prove unrecorded runtime/workload scope or missing peak bounds. Explicit
12
+ `resource_bounds` must be scoped by the caller to the selected artifact,
13
+ runtime, placement and workload. There is currently no persisted producer of
14
+ complete backend bounds, so legacy Transformers facts alone cannot certify a load.
15
+
16
+ `imports.functions.runtime.derive` produces conditional loading and inference tensor reservations
17
+ from shared config geometry and selected safetensors metadata. Wrapper collects
18
+ the largest selected shard and largest BF16/FP16 destination tensor using
19
+ headers/stat only. The same derivation feeds adapter and sizing requirements.
20
+ It does not substitute an observed after-load size for a loading peak. See
21
+ [runtime derivation](RUNTIME_DERIVATION.md) for equations, scope and actual
22
+ inventory results. Backend workspace, loader workspace and allocator overhead
23
+ remain named unknowns until supported evidence supplies them.
24
+
25
+ Wrapper attaches these records to `ModelFootprint`. Unknown requirements refuse
26
+ before resident replacement; the pure commit check compares complete selected
27
+ requirements with captured device/host budgets before unloading victims. The
28
+ existing eviction eligibility and ordering modules retain victim authority.
29
+
30
+ Install this package alongside staged Discovery and Wrapper sources. Wrapper's
31
+ offline Debian builder includes its wheel. Source changes are not deployed by
32
+ editing these files. No tests or runtime load validation were run for this
33
+ refactor; existing database validation records belong to the previous revision.
34
+
35
+ ## Source organization
36
+
37
+ The package source is `src/hugpy_resources`. Standard-library and external
38
+ imports go through `imports/init_imports.py`; shared constants live in
39
+ `imports/constants/`. Reusable arithmetic lives in `imports/functions/`,
40
+ grouped into integer validation, tensors, KV cache, recurrent state,
41
+ quantization, placement, memory budgets, configuration, geometry, runtime
42
+ envelopes, evidence, cache ladders, feasibility and requirements.
43
+
44
+ Import implementations directly from `hugpy_resources.imports.functions` or a
45
+ specific module in that package. The former `formulas`, `geometry`, `devices`,
46
+ `runtime`, and `requirements` forwarding modules have been removed. Internal
47
+ imports are relative; shared tables and vocabularies live in `imports/constants/`.
48
+ Optional parser dependencies load only when the corresponding format is read.
49
+
50
+ ## Generic function reference
51
+
52
+ Resources is the shared reference for generic utilities used by Discovery.
53
+ Implementations live in `src/hugpy_resources/imports/functions/`; generic
54
+ constants, parsing errors and MIME data live beside them under `imports/`.
55
+
56
+ | Script | Responsibility |
57
+ | --- | --- |
58
+ | `json_utils.py` | Stable JSON, duplicate-key validation and content digests |
59
+ | `formatting.py` | Human-readable byte counts |
60
+ | `ast_utils.py` | Dotted AST names and JSON-compatible literals |
61
+ | `paths.py` | Path resolution, metadata snapshots and repository-relative paths |
62
+ | `locking.py` | Nonblocking exclusive file locks |
63
+ | `sqlite.py`, `sqlite_connections.py` | SQL identifiers, row writes, read-only connections and WAL retry |
64
+ | `file_types.py` | MIME hints and bounded file-format identification |
65
+ | `tensor_files.py` | Header-only tensor inspection, separate from `tensors.py` arithmetic |
66
+ | `gguf_headers.py`, `gguf_moe.py`, `gguf_structure.py` | GGUF headers, integrity, expert tensors and geometry |
67
+ | `content_proofs.py` | Stable-file SHA256 and Git-blob SHA1, without queues or publication |
68
+ | `source_fingerprints.py` | Deterministic package code/data fingerprints |
69
+
70
+ Use `hugpy_resources.imports` or a specific implementation module. Optional
71
+ SentencePiece/protobuf decoding is installed with `hugpy-resources[inspection]`
72
+ and imported only for that inspection. Generic helpers do not import Discovery,
73
+ Storage, Wrapper or a provider SDK. Consumers supply workflow policy; for
74
+ example, `directory_snapshot(root, excluded_directories=...)` accepts the
75
+ consumer's exclusions.
76
+
77
+
78
+ ## Tools consume shared implementations
79
+
80
+ `hugpy-tools` imports Resources for its generic file/path operations, structured
81
+ data, hashing, text, time and local search engine. Consumers import Resources directly. The old generic Tools modules are
82
+ removed; tool registration, input schemas, dispatch and backend adapters stay
83
+ in Tools. Resources never imports Tools.
84
+
85
+ Additional generic groups are `filesystem.py`, `hashing.py`,
86
+ `structured_data.py`, `text.py`, `time_utils.py`, `search.py` and `processes.py`.
87
+ Path operations share `paths.py`; safe JSON dumping/key traversal share
88
+ `json_utils.py`; common limits and patterns live in `imports/constants/`.
89
+ `PathEscape` is defined once in `imports/classes.py`.
90
+
91
+ Search's `read_text` keeps its own search-specific result contract; the shared
92
+ `imports.read_text` export names the filesystem reader. Use the search module
93
+ for search operations. Token estimation remains approximate; no model or
94
+ provider package is loaded by the base implementation. YAML parsing is optional
95
+ through `hugpy-resources[yaml]` or Tools' existing YAML extra.
96
+
97
+ `./ops.sh local-pip-update --plan` shows a local install plan. Local installs
98
+ build and archive a new numeric revision. `./ops.sh push-to-pypi --plan` and
99
+ `./ops.sh push-to-github --plan` show the existing release actions; Tools' push
100
+ plan runs Resources first.
101
+
102
+ Generic argument parsing (`coerce_args`, `parse_bool`), SQL quoting
103
+ (`sql_identifier`, `sql_literal`), and explicit environment-file parsing
104
+ (`environment`, `home_dir`, `expand_home`, `parse_env_file`) are grouped under
105
+ `imports/functions/`. `terminal_text.py` supplies shared terminal sanitization,
106
+ Unicode cell widths, clipping, and wrapping; the shared export surface uses
107
+ `clean_terminal_text`, `terminal_cell_width`, `terminal_text_width`,
108
+ `cut_terminal_text`, and `wrap_terminal_text` to distinguish these operations
109
+ from generic text processing.
110
+
111
+ ## Suite nomenclature
112
+
113
+ [Resource and storage nomenclature](docs/nomenclature.md) defines store roles,
114
+ T0/T1/T2 capacity tiers, allocation accounting, module ownership, and the
115
+ non-expert-first GPU priority for spill placement. Import `TERM_LABELS`,
116
+ `TERM_DEFINITIONS`, and `PLACEMENT_TERM_ALIASES` from `hugpy_resources.imports`
117
+ for shared strings. MoE names architecture; Spill names the placement policy.
118
+ Legacy placement identifiers remain compatible; the glossary distinguishes
119
+ that policy contract from backend implementation status.
120
+
121
+ `response_shape` is the pure response-structure validator used by Wrapper's
122
+ engine capability probes. It checks chat, embeddings, and explicitly required
123
+ JSON keys without launching engines or interpreting a pass as general model
124
+ compatibility. Probe orchestration and evidence persistence remain in Wrapper.
125
+
126
+ ## Consolidated runtime primitives
127
+
128
+ Wrapper, Discovery and Toolserver share implementations below the application
129
+ layer. `imports/classes/` owns exceptions, row/cursor adapters, HTTP scaffolding,
130
+ NVML structures and stream splitters. `imports/constants/` separates evidence
131
+ contracts from shared platform, format and protocol contracts.
132
+
133
+ Environment overrides and application paths live in `env_values`, `environment`
134
+ and `application_paths`. CPU/process/host/NVML readers, model config/identity,
135
+ Comfy declarations, GGUF shard grouping, strict safetensors selection, inference
136
+ timings, reasoning and tool-call parsing are grouped by purpose under
137
+ `imports/functions/`. Similar APIs with different error or identity semantics
138
+ remain separate. `python_environments.provision_python_environment` performs
139
+ explicit installation only when called; the caller owns package/version policy.
140
+
141
+ Resources has no mandatory backend or application dependencies. Standard and
142
+ external imports resolve through `imports/init_imports.py`. Hardware libraries,
143
+ model backends and optional parsers remain deferred to their feature call sites.
144
+ Wrapper-specific placement and quantization preference policies remain in Wrapper.
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hugpy-resources"
7
+ version = "0.1.3"
8
+ description = "Shared generic utilities, file inspection and model resource evidence contracts"
9
+ requires-python = ">=3.9"
10
+ dependencies = []
11
+
12
+ [tool.setuptools.packages.find]
13
+ where = ["src"]
14
+
15
+ [project.optional-dependencies]
16
+ inspection = ["sentencepiece>=0.2", "protobuf>=4"]
17
+ yaml = ["PyYAML"]
18
+
19
+ [tool.setuptools.package-data]
20
+ "hugpy_resources.imports" = ["*.json"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Pure resource contracts; no engines, database connections or hardware probes."""
2
+
3
+
@@ -0,0 +1,35 @@
1
+ """Public shared constants, classes and generic utilities.
2
+
3
+ Function/class ownership is declared only in their projections. Constants load
4
+ on first use; optional function implementations load only when requested.
5
+ """
6
+
7
+ from importlib import import_module as _import_module
8
+
9
+ from . import classes as _classes
10
+ from . import functions as _functions
11
+
12
+
13
+ def __getattr__(name):
14
+ if name == "__all__":
15
+ constants = _import_module(f"{__name__}.constants")
16
+ value = list(dict.fromkeys([
17
+ *constants.__all__, *_functions.__all__, *_classes.__all__,
18
+ ]))
19
+ elif name in _functions.__all__:
20
+ # A loaded child module can have the same name as an exported function
21
+ # (for example environment). Resolve the declared owner explicitly.
22
+ value = _functions.__getattr__(name)
23
+ elif name in _classes.__all__:
24
+ value = _classes.__getattr__(name)
25
+ else:
26
+ constants = _import_module(f"{__name__}.constants")
27
+ if name not in constants.__all__:
28
+ raise AttributeError(f"{__name__!r} has no attribute {name!r}") from None
29
+ value = getattr(constants, name)
30
+ globals()[name] = value
31
+ return value
32
+
33
+
34
+ def __dir__():
35
+ return sorted(set(globals()) | set(__getattr__("__all__")))
@@ -0,0 +1,24 @@
1
+ """Canonical reusable errors, records, protocol parsers and HTTP scaffold."""
2
+
3
+ from hugpy_resources.lazy_imports import import_registry, resolve_import
4
+
5
+ from ..init_imports import import_module
6
+
7
+ _EXPORTS = import_registry({
8
+ '.errors': (
9
+ 'GGUFHeaderError PathEscape LocalRequestError ChatRequestError TaskRequestError '
10
+ 'StoreError'
11
+ ),
12
+ '.database': 'Row Cursor',
13
+ '.nvml': '_MemV2 _Mem _ProcV3',
14
+ '.thinking': 'ThinkSplitter',
15
+ '.tool_calls': 'StreamSplitter',
16
+ '.http': 'make_child_handler',
17
+ })
18
+
19
+
20
+ def __getattr__(name):
21
+ return resolve_import(globals(), _EXPORTS, name)
22
+
23
+
24
+ __all__ = list(_EXPORTS)
@@ -0,0 +1,49 @@
1
+ """Reusable database definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class Row:
7
+ """sqlite3.Row compatible access, including duplicate projection names."""
8
+
9
+ def __init__(self, names, values):
10
+ self._names, self._values = tuple(names), tuple(values)
11
+
12
+ def __getitem__(self, key):
13
+ if isinstance(key, (int, slice)):
14
+ return self._values[key]
15
+ try:
16
+ return self._values[self._names.index(key)]
17
+ except ValueError:
18
+ raise KeyError(key) from None
19
+
20
+ def __iter__(self):
21
+ return iter(self._values)
22
+
23
+ def __len__(self):
24
+ return len(self._values)
25
+
26
+ def keys(self):
27
+ return self._names
28
+
29
+ def get(self, key, default=None):
30
+ try:
31
+ return self[key]
32
+ except KeyError:
33
+ return default
34
+
35
+
36
+ class Cursor:
37
+ def __init__(self, rows, rowcount=0, lastrowid=None, columns=(), values=()):
38
+ self.rows = iter(
39
+ [Row(columns, row) for row in values]
40
+ if columns
41
+ else [Row(row.keys(), row.values()) for row in rows]
42
+ )
43
+ self.rowcount, self.lastrowid = rowcount, lastrowid
44
+
45
+ def fetchone(self):
46
+ return next(self.rows, None)
47
+
48
+ def fetchall(self):
49
+ return list(self.rows)
@@ -0,0 +1,74 @@
1
+ """Reusable parsing errors."""
2
+
3
+
4
+ class GGUFHeaderError(ValueError):
5
+ """The GGUF header is unreadable (magic, version, extent or EOF)."""
6
+
7
+
8
+ __all__ = ["GGUFHeaderError"]
9
+
10
+
11
+ class PathEscape(ValueError):
12
+ """A path resolved outside the confinement root."""
13
+
14
+
15
+ __all__ += ["PathEscape"]
16
+
17
+
18
+ class LocalRequestError(Exception):
19
+ """Fallback request error so a runner / child module is importable and
20
+ py_compile-clean on its own (e.g. a unit test imports ``handle`` directly).
21
+
22
+ The child process INJECTS its own ``RequestError`` class into the runner's
23
+ namespace before serving; that class carries the same ``(status, message)``
24
+ and a ``.body`` dict, so ``raise RequestError(400, …)`` stays catchable.
25
+ """
26
+
27
+ def __init__(self, status: int, message: str, **extra):
28
+ super().__init__(message)
29
+ self.status = int(status)
30
+ self.message = message
31
+ self.extra = extra
32
+
33
+ @property
34
+ def body(self) -> dict:
35
+ err = {
36
+ "message": self.message,
37
+ "type": "invalid_request_error",
38
+ "code": self.status,
39
+ }
40
+ err.update(self.extra)
41
+ return {"error": err}
42
+
43
+
44
+ class ChatRequestError(Exception):
45
+ """An OpenAI-style client error: HTTP ``status`` + error body."""
46
+
47
+ def __init__(
48
+ self, status: int, message: str, etype: str = "invalid_request_error", **extra
49
+ ):
50
+ super().__init__(message)
51
+ self.status = status
52
+ self.body = {
53
+ "error": {"code": status, "type": etype, "message": message, **extra}
54
+ }
55
+
56
+
57
+ class TaskRequestError(Exception):
58
+ """An OpenAI-style client error: HTTP ``status`` + error body. INJECTED into
59
+ the runner module's namespace before use, so a runner raises
60
+ ``RequestError(400, …)`` and it is caught here as a 4xx (never a 500)."""
61
+
62
+ def __init__(
63
+ self, status: int, message: str, etype: str = "invalid_request_error", **extra
64
+ ):
65
+ super().__init__(message)
66
+ self.status = int(status)
67
+ self.message = message
68
+ self.body = {
69
+ "error": {"code": int(status), "type": etype, "message": message, **extra}
70
+ }
71
+
72
+
73
+ class StoreError(RuntimeError):
74
+ """The store could not be opened or migrated."""
@@ -0,0 +1,116 @@
1
+ """HTTP scaffold for engine children."""
2
+
3
+ from __future__ import annotations
4
+ from ..init_imports import json
5
+ from ..init_imports import traceback
6
+ from ..init_imports import BaseHTTPRequestHandler
7
+
8
+
9
+ def _not_found(path: str) -> dict:
10
+ return {
11
+ "error": {"code": 404, "type": "not_found_error", "message": f"no route {path}"}
12
+ }
13
+
14
+
15
+ def make_child_handler(server, request_error_cls, *, chat: bool = True, log=None):
16
+ """Build the ``BaseHTTPRequestHandler`` subclass the child model servers use.
17
+
18
+ Shared across tf_child / vision_child / task_child: ``log_message`` (silent —
19
+ stderr is the loader-log channel), ``_json``, and the GET routes
20
+ (``/health``, ``/v1/models``, ``/props``). With ``chat=True`` (tf/vision) the
21
+ POST route is ``/v1/chat/completions``, errors fall back to a 500 (logged via
22
+ ``log`` if given), and the subclass supplies ``_stream``; with ``chat=False``
23
+ (task) the subclass supplies its own ``do_POST``.
24
+
25
+ ``server`` must expose ``models_doc()``/``props_doc()`` and, for chat,
26
+ ``lock``/``prepare(body)``/``complete(job)``. ``request_error_cls`` is the
27
+ child's injected ``RequestError``; ``log`` is the child's ``_log``.
28
+ """
29
+ RequestError = request_error_cls
30
+
31
+ class _Handler(BaseHTTPRequestHandler):
32
+ protocol_version = "HTTP/1.0"
33
+
34
+ def log_message(self, fmt, *args): # keep the loader log for errors only
35
+ pass
36
+
37
+ def _json(self, status: int, doc: dict) -> None:
38
+ data = json.dumps(doc).encode()
39
+ self.send_response(status)
40
+ self.send_header("Content-Type", "application/json")
41
+ self.send_header("Content-Length", str(len(data)))
42
+ self.end_headers()
43
+ self.wfile.write(data)
44
+
45
+ def do_GET(self):
46
+ path = self.path.split("?", 1)[0]
47
+ if path == "/health":
48
+ return self._json(200, {"status": "ok"})
49
+ if path == "/v1/models":
50
+ return self._json(200, server.models_doc())
51
+ if path == "/props":
52
+ return self._json(200, server.props_doc())
53
+ return self._json(404, _not_found(path))
54
+
55
+ if chat:
56
+
57
+ def do_POST(self):
58
+ path = self.path.split("?", 1)[0]
59
+ if path not in ("/v1/chat/completions", "/tokenize"):
60
+ return self._json(404, _not_found(path))
61
+ try:
62
+ n = int(self.headers.get("Content-Length") or 0)
63
+ body = json.loads(self.rfile.read(n) or b"{}")
64
+ if not isinstance(body, dict):
65
+ raise RequestError(400, "request body must be a JSON object")
66
+ except (ValueError, RequestError) as exc:
67
+ err = (
68
+ exc
69
+ if isinstance(exc, RequestError)
70
+ else RequestError(400, f"bad JSON: {exc}")
71
+ )
72
+ return self._json(err.status, err.body)
73
+ if path == "/tokenize":
74
+ tokenize = getattr(server, "tokenize_doc", None)
75
+ if tokenize is None:
76
+ return self._json(404, _not_found(path))
77
+ if not server.lock.acquire(blocking=False):
78
+ return self._json(
79
+ 409,
80
+ {
81
+ "error": {
82
+ "message": "Tokenizer preflight deferred while the engine is busy"
83
+ }
84
+ },
85
+ )
86
+ try:
87
+ return self._json(200, tokenize(body))
88
+ except RequestError as exc:
89
+ return self._json(exc.status, exc.body)
90
+ except Exception as exc:
91
+ return self._json(400, {"error": {"message": str(exc)}})
92
+ finally:
93
+ server.lock.release()
94
+ with server.lock:
95
+ try:
96
+ job = server.prepare(body)
97
+ if not body.get("stream"):
98
+ return self._json(200, server.complete(job))
99
+ return self._stream(job)
100
+ except RequestError as exc:
101
+ return self._json(exc.status, exc.body)
102
+ except Exception as exc: # noqa: BLE001 — the real error, to the caller and the log
103
+ if log:
104
+ log(traceback.format_exc())
105
+ return self._json(
106
+ 500,
107
+ {
108
+ "error": {
109
+ "code": 500,
110
+ "type": "server_error",
111
+ "message": f"{type(exc).__name__}: {exc}",
112
+ }
113
+ },
114
+ )
115
+
116
+ return _Handler
@@ -0,0 +1,32 @@
1
+ """Reusable nvml definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..init_imports import ctypes
6
+
7
+
8
+ class _MemV2(ctypes.Structure):
9
+ _fields_ = [
10
+ ("version", ctypes.c_uint),
11
+ ("total", ctypes.c_ulonglong),
12
+ ("reserved", ctypes.c_ulonglong),
13
+ ("free", ctypes.c_ulonglong),
14
+ ("used", ctypes.c_ulonglong),
15
+ ]
16
+
17
+
18
+ class _Mem(ctypes.Structure):
19
+ _fields_ = [
20
+ ("total", ctypes.c_ulonglong),
21
+ ("free", ctypes.c_ulonglong),
22
+ ("used", ctypes.c_ulonglong),
23
+ ]
24
+
25
+
26
+ class _ProcV3(ctypes.Structure):
27
+ _fields_ = [
28
+ ("pid", ctypes.c_uint),
29
+ ("usedGpuMemory", ctypes.c_ulonglong),
30
+ ("gpuInstanceId", ctypes.c_uint),
31
+ ("computeInstanceId", ctypes.c_uint),
32
+ ]
@@ -0,0 +1,77 @@
1
+ # Streaming sibling of no_think.strip_think: separate a model's <think> reasoning
2
+ # from its answer prose INCREMENTALLY, as content deltas arrive, so the front
3
+ # door can re-emit delta.reasoning_content vs delta.content live. The engine
4
+ # (llama-server --reasoning-format deepseek) usually separates reasoning itself;
5
+ # this is the fallback for inline-tag replies (reasoning-format none / a model
6
+ # whose template leaves <think> tags in message.content).
7
+ #
8
+ # Final-assembly semantics match no_think.strip_think (<think>...</think> blocks
9
+ # and an unclosed <think>...EOF are reasoning). An ORPHAN </think> (a template
10
+ # that opens <think> in the prompt, so the reply starts mid-reasoning with no
11
+ # opening tag) is handled downstream by hugpy_core.grader.strip_think on the
12
+ # assembled text; live, text before an orphan close streams as content and the
13
+ # answer after it streams as content too — the grader's rule salvages the answer.
14
+ """Incremental <think> splitter for streamed content."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from ..constants import THINK_CLOSE_TAG as _CLOSE
19
+ from ..constants import THINK_OPEN_TAG as _OPEN
20
+
21
+
22
+ from ..functions.thinking import _partial_tail
23
+
24
+
25
+ class ThinkSplitter:
26
+ """Feed streamed content text; get back (channel, chunk) pairs where channel
27
+ is 'reasoning' (inside a <think> block) or 'content' (the answer). Holds back
28
+ a short tail when the buffer might end mid-tag, so a tag split across two
29
+ feeds is still recognised."""
30
+
31
+ def __init__(self) -> None:
32
+ self._buf = ""
33
+ self._mode = "content" # 'content' | 'reasoning'
34
+
35
+ def feed(self, text: str) -> "list[tuple[str, str]]":
36
+ out: "list[tuple[str, str]]" = []
37
+ if not text:
38
+ return out
39
+ self._buf += text
40
+ while True:
41
+ low = self._buf.lower()
42
+ i_open = low.find(_OPEN)
43
+ i_close = low.find(_CLOSE)
44
+ cands = [p for p in (i_open, i_close) if p != -1]
45
+ if cands:
46
+ idx = min(cands)
47
+ is_open = idx == i_open
48
+ tag = _OPEN if is_open else _CLOSE
49
+ before = self._buf[:idx]
50
+ if before:
51
+ out.append((self._mode, before))
52
+ self._buf = self._buf[idx + len(tag) :]
53
+ self._mode = "reasoning" if is_open else "content"
54
+ continue
55
+ hold = _partial_tail(self._buf)
56
+ if hold:
57
+ emit = self._buf[: len(self._buf) - hold]
58
+ if emit:
59
+ out.append((self._mode, emit))
60
+ self._buf = self._buf[len(self._buf) - hold :]
61
+ else:
62
+ if self._buf:
63
+ out.append((self._mode, self._buf))
64
+ self._buf = ""
65
+ break
66
+ return out
67
+
68
+ def close(self) -> "list[tuple[str, str]]":
69
+ """Flush whatever is held (a dangling partial tag, treated as text)."""
70
+ out: "list[tuple[str, str]]" = []
71
+ if self._buf:
72
+ out.append((self._mode, self._buf))
73
+ self._buf = ""
74
+ return out
75
+
76
+
77
+ __all__ = ["ThinkSplitter"]
@@ -0,0 +1,53 @@
1
+ """Reusable tool calls definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..functions.tool_calls import _calls
6
+ from ..constants import _BLOCK
7
+ from ..constants import DSML_TOOLS_TAGS as _TAGS
8
+
9
+
10
+ class StreamSplitter:
11
+ """Streamed reply: feed() content pieces; yields ("content", text) for prose and
12
+ ("tool_calls", [call, ...]) once a whole block has arrived. Text that may be the
13
+ start of a block is held back until it is known not to be one."""
14
+
15
+ def __init__(self):
16
+ self.buf = ""
17
+ self.emitted = 0 # tool calls sent so far (their stream index)
18
+
19
+ def _hold_from(self) -> int:
20
+ """Index from which the buffer must be held (a block, or a possible tag start)."""
21
+ starts = [i for tag in _TAGS if (i := self.buf.find(tag)) >= 0]
22
+ if starts:
23
+ return min(starts)
24
+ for k in range(min(max(map(len, _TAGS)) - 1, len(self.buf)), 0, -1):
25
+ if any(tag.startswith(self.buf[-k:]) for tag in _TAGS):
26
+ return len(self.buf) - k
27
+ return len(self.buf)
28
+
29
+ def feed(self, piece: str):
30
+ self.buf += piece or ""
31
+ out = []
32
+ while True:
33
+ m = _BLOCK.search(self.buf)
34
+ if m:
35
+ if m.start():
36
+ out.append(("content", self.buf[: m.start()]))
37
+ calls = _calls(m.group(2))
38
+ if calls:
39
+ out.append(("tool_calls", calls))
40
+ else:
41
+ out.append(("content", m.group(0)))
42
+ self.buf = self.buf[m.end() :]
43
+ continue
44
+ cut = self._hold_from()
45
+ if cut:
46
+ out.append(("content", self.buf[:cut]))
47
+ self.buf = self.buf[cut:]
48
+ return out
49
+
50
+ def close(self):
51
+ """End of stream: an unfinished block is passed through as text (never lost)."""
52
+ rest, self.buf = self.buf, ""
53
+ return [("content", rest)] if rest else []