hypernix 0.45.0__tar.gz → 0.45.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 (84) hide show
  1. {hypernix-0.45.0 → hypernix-0.45.2}/PKG-INFO +1 -1
  2. {hypernix-0.45.0 → hypernix-0.45.2}/pyproject.toml +1 -1
  3. {hypernix-0.45.0 → hypernix-0.45.2}/setup.cfg +1 -1
  4. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/__init__.py +1 -1
  5. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/pans.py +86 -19
  6. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_kitchen_v044.py +106 -0
  7. {hypernix-0.45.0 → hypernix-0.45.2}/.github/workflows/build.yml +0 -0
  8. {hypernix-0.45.0 → hypernix-0.45.2}/.github/workflows/ci.yml +0 -0
  9. {hypernix-0.45.0 → hypernix-0.45.2}/.github/workflows/public-release.yml +0 -0
  10. {hypernix-0.45.0 → hypernix-0.45.2}/.github/workflows/release.yml +0 -0
  11. {hypernix-0.45.0 → hypernix-0.45.2}/LICENSE +0 -0
  12. {hypernix-0.45.0 → hypernix-0.45.2}/MANIFEST.in +0 -0
  13. {hypernix-0.45.0 → hypernix-0.45.2}/README.md +0 -0
  14. {hypernix-0.45.0 → hypernix-0.45.2}/examples/custom_arch.py +0 -0
  15. {hypernix-0.45.0 → hypernix-0.45.2}/examples/quickstart.py +0 -0
  16. {hypernix-0.45.0 → hypernix-0.45.2}/examples/run_hypernix.py +0 -0
  17. {hypernix-0.45.0 → hypernix-0.45.2}/examples/train_hypernix_0_1_5_evaluator.py +0 -0
  18. {hypernix-0.45.0 → hypernix-0.45.2}/examples/train_hypernix_1_5_gtx1080.py +0 -0
  19. {hypernix-0.45.0 → hypernix-0.45.2}/examples/upload_to_hub.py +0 -0
  20. {hypernix-0.45.0 → hypernix-0.45.2}/pytest.ini +0 -0
  21. {hypernix-0.45.0 → hypernix-0.45.2}/ruff.toml +0 -0
  22. {hypernix-0.45.0 → hypernix-0.45.2}/scripts/install_deps.sh +0 -0
  23. {hypernix-0.45.0 → hypernix-0.45.2}/scripts/quantize_i7_7660u.sh +0 -0
  24. {hypernix-0.45.0 → hypernix-0.45.2}/setup.py +0 -0
  25. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/__main__.py +0 -0
  26. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/arch.py +0 -0
  27. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/blender.py +0 -0
  28. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/cli.py +0 -0
  29. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/coffee_maker.py +0 -0
  30. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/convert.py +0 -0
  31. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/deps.py +0 -0
  32. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/doctor.py +0 -0
  33. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/download.py +0 -0
  34. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/espresso_maker.py +0 -0
  35. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/fetcher.py +0 -0
  36. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/food_processor.py +0 -0
  37. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/freezer.py +0 -0
  38. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/generate.py +0 -0
  39. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/industrial_range.py +0 -0
  40. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/instant_pot.py +0 -0
  41. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/mediocre_fridge.py +0 -0
  42. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/microwave.py +0 -0
  43. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/nano_nano.py +0 -0
  44. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/new_fridge.py +0 -0
  45. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/new_range.py +0 -0
  46. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/old_fridge.py +0 -0
  47. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/old_oven.py +0 -0
  48. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/old_range.py +0 -0
  49. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/pressure_cooker.py +0 -0
  50. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/py.typed +0 -0
  51. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/quantize.py +0 -0
  52. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/sink.py +0 -0
  53. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/smoke_alarm.py +0 -0
  54. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/smoker.py +0 -0
  55. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/table.py +0 -0
  56. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/toaster.py +0 -0
  57. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/train.py +0 -0
  58. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix/upload.py +0 -0
  59. {hypernix-0.45.0 → hypernix-0.45.2}/src/hypernix.egg-info/SOURCES.txt +0 -0
  60. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_alarms_and_presets.py +0 -0
  61. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_arch_mapping.py +0 -0
  62. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_cli_integration.py +0 -0
  63. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_deps_and_windows.py +0 -0
  64. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_fetcher.py +0 -0
  65. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_freezer.py +0 -0
  66. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_fridges.py +0 -0
  67. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_kitchen_v045.py +0 -0
  68. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_new_archs_v035.py +0 -0
  69. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_old_oven.py +0 -0
  70. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_ranges.py +0 -0
  71. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_snapshot_roundtrip.py +0 -0
  72. {hypernix-0.45.0 → hypernix-0.45.2}/tests/test_v031_chat_and_archs.py +0 -0
  73. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Alarms.md +0 -0
  74. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Architectures.md +0 -0
  75. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/CLI.md +0 -0
  76. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Freezer.md +0 -0
  77. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Fridges.md +0 -0
  78. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Home.md +0 -0
  79. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Kitchen.md +0 -0
  80. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Ovens.md +0 -0
  81. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Pascal.md +0 -0
  82. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Quantization.md +0 -0
  83. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Ranges.md +0 -0
  84. {hypernix-0.45.0 → hypernix-0.45.2}/wiki/Training.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypernix
3
- Version: 0.45.0
3
+ Version: 0.45.2
4
4
  Summary: Download and quantize the HyperNix PyTorch model to GGUF (fp32/fp16/Q8_0/Q6_K/Q4_K_M).
5
5
  Home-page: https://github.com/minerofthesoal/hypernix-pip
6
6
  Author: HyperNix contributors
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hypernix"
7
- version = "0.45.0"
7
+ version = "0.45.2"
8
8
  description = "Download and quantize the HyperNix PyTorch model to GGUF (fp32/fp16/Q8_0/Q6_K/Q4_K_M)."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = hypernix
3
- version = 0.45.0
3
+ version = 0.45.2
4
4
  description = Download and quantize the HyperNix PyTorch model to GGUF (fp32/fp16/Q8_0/Q6_K/Q4_K_M).
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -157,5 +157,5 @@ __all__ = [
157
157
  "verify_snapshot",
158
158
  ]
159
159
 
160
- __version__ = "0.45.0"
160
+ __version__ = "0.45.2"
161
161
  DEFAULT_REPO_ID = "ray0rf1re/hyper-nix.1"
@@ -25,6 +25,7 @@ import re
25
25
  from collections.abc import Iterable, Iterator
26
26
  from dataclasses import dataclass, field
27
27
  from pathlib import Path
28
+ from typing import Any, ClassVar
28
29
 
29
30
  _WS_RE = re.compile(r"[ \t]+")
30
31
  _MULTINEWLINE_RE = re.compile(r"\n{3,}")
@@ -37,10 +38,41 @@ _MULTINEWLINE_RE = re.compile(r"\n{3,}")
37
38
  @dataclass
38
39
  class Pan:
39
40
  """Abstract pan. Subclasses override :meth:`cook` (one line in,
40
- zero-or-one line out) or :meth:`iter` (full iterator override)."""
41
+ zero-or-one line out) or :meth:`iter` (full iterator override).
42
+
43
+ ``name`` is deliberately a :class:`ClassVar` rather than a
44
+ ``@dataclass`` field — it's a human-readable label used in
45
+ ``__repr__`` / logs, not state. Making it a field exposed it as
46
+ the *second positional argument* of every subclass, which meant
47
+ ``Skillet("file.txt", "instruct")`` silently set ``name="instruct"``
48
+ instead of ``mode="instruct"`` and left the caller confused.
49
+
50
+ Every pan accepts two *keyword-only* length caps:
51
+
52
+ * ``max_chars`` — hard per-line character cap. Longer lines are
53
+ truncated (not split).
54
+ * ``context_length`` — convenience for training-script callers.
55
+ Treated as an approximate token budget and converted to
56
+ ``max_chars = context_length * 4`` (a reasonable English-BPE
57
+ heuristic). Takes precedence over ``max_chars`` when both are
58
+ set. For precise chunking by tokens / chars use
59
+ :mod:`hypernix.food_processor` (``SliceBlade`` / ``ShredBlade``)
60
+ instead.
61
+ """
41
62
 
42
63
  source: Path | str | Iterable[str]
43
- name: str = "Pan"
64
+ max_chars: int | None = field(default=None, kw_only=True)
65
+ context_length: int | None = field(default=None, kw_only=True)
66
+ name: ClassVar[str] = "Pan"
67
+
68
+ # Rough chars-per-token ratio for English BPE tokenizers. Only
69
+ # used when ``context_length`` is set and ``max_chars`` is not.
70
+ _CHARS_PER_TOKEN: ClassVar[int] = 4
71
+
72
+ def _effective_max_chars(self) -> int | None:
73
+ if self.context_length is not None:
74
+ return self.context_length * self._CHARS_PER_TOKEN
75
+ return self.max_chars
44
76
 
45
77
  def _source_lines(self) -> Iterator[str]:
46
78
  if isinstance(self.source, (str, Path)):
@@ -55,10 +87,14 @@ class Pan:
55
87
  return line
56
88
 
57
89
  def iter(self) -> Iterator[str]:
90
+ cap = self._effective_max_chars()
58
91
  for raw in self._source_lines():
59
92
  out = self.cook(raw)
60
- if out is not None:
61
- yield out
93
+ if out is None:
94
+ continue
95
+ if cap is not None and len(out) > cap:
96
+ out = out[:cap]
97
+ yield out
62
98
 
63
99
  def __iter__(self) -> Iterator[str]:
64
100
  return self.iter()
@@ -74,7 +110,7 @@ class FryingPan(Pan):
74
110
  through. Useful when the input is already clean (already-formatted
75
111
  corpora, pre-tokenized chunks)."""
76
112
 
77
- name: str = "FryingPan"
113
+ name: ClassVar[str] = "FryingPan"
78
114
 
79
115
  def cook(self, line: str) -> str | None:
80
116
  return line.rstrip()
@@ -90,7 +126,7 @@ class SaucePan(Pan):
90
126
  strip leading/trailing whitespace. Standard mild cleaning for
91
127
  scraped or OCR'd text."""
92
128
 
93
- name: str = "SaucePan"
129
+ name: ClassVar[str] = "SaucePan"
94
130
 
95
131
  def cook(self, line: str) -> str | None:
96
132
  line = _WS_RE.sub(" ", line.strip())
@@ -111,10 +147,10 @@ class Skillet(Pan):
111
147
  replies on the following line (or in a separate corpus).
112
148
  """
113
149
 
114
- name: str = "Skillet"
115
150
  mode: str = "chat" # "chat" | "instruct"
116
151
  user_tag: str = "<USER>"
117
152
  assistant_tag: str = "<ASSISTANT>"
153
+ name: ClassVar[str] = "Skillet"
118
154
 
119
155
  def cook(self, line: str) -> str | None:
120
156
  line = line.strip()
@@ -133,11 +169,15 @@ class Skillet(Pan):
133
169
  class GrillPan(Pan):
134
170
  """High direct heat. SaucePan-style cleaning plus deduplication
135
171
  (SHA1 hash set) and a minimum length filter. Safe on arbitrary
136
- web-scraped text: collapses boilerplate, drops single-word lines."""
172
+ web-scraped text: collapses boilerplate, drops single-word lines.
173
+
174
+ ``_seen`` is internal dedupe state and is not part of the public
175
+ ``__init__`` signature.
176
+ """
137
177
 
138
- name: str = "GrillPan"
139
178
  min_chars: int = 8
140
- _seen: set[str] = field(default_factory=set, repr=False)
179
+ name: ClassVar[str] = "GrillPan"
180
+ _seen: set[str] = field(default_factory=set, repr=False, init=False)
141
181
 
142
182
  def cook(self, line: str) -> str | None:
143
183
  line = _WS_RE.sub(" ", line.strip())
@@ -162,13 +202,14 @@ class Wok(Pan):
162
202
  small corpora where order-randomization matters and you can afford
163
203
  the memory."""
164
204
 
165
- name: str = "Wok"
166
205
  seed: int = 0
167
206
  reverse_ratio: float = 0.0
207
+ name: ClassVar[str] = "Wok"
168
208
 
169
209
  def iter(self) -> Iterator[str]:
170
210
  # Pre-clean with SaucePan semantics.
171
211
  rng = random.Random(self.seed)
212
+ cap = self._effective_max_chars()
172
213
  buffer: list[str] = []
173
214
  for raw in self._source_lines():
174
215
  line = _WS_RE.sub(" ", raw.strip())
@@ -176,10 +217,14 @@ class Wok(Pan):
176
217
  buffer.append(line)
177
218
  rng.shuffle(buffer)
178
219
  for line in buffer:
179
- if self.reverse_ratio > 0 and rng.random() < self.reverse_ratio:
180
- yield " ".join(reversed(line.split()))
181
- else:
182
- yield line
220
+ out = (
221
+ " ".join(reversed(line.split()))
222
+ if self.reverse_ratio > 0 and rng.random() < self.reverse_ratio
223
+ else line
224
+ )
225
+ if cap is not None and len(out) > cap:
226
+ out = out[:cap]
227
+ yield out
183
228
 
184
229
 
185
230
  # ---------------------------------------------------------------------------
@@ -195,7 +240,29 @@ TIERS: dict[str, type[Pan]] = {
195
240
  }
196
241
 
197
242
 
198
- def pick_pan(tier: str, source: Path | str | Iterable[str], **kwargs: str) -> Pan:
199
- """Return a pan instance by tier name (``"frying-pan"``..``"wok"``)."""
200
- cls = TIERS[tier.lower().replace("_", "-")]
201
- return cls(source=source, **kwargs)
243
+ def pick_pan(tier: str, source: Path | str | Iterable[str], **kwargs: Any) -> Pan:
244
+ """Return a pan instance by tier name (``"frying-pan"``..``"wok"``).
245
+
246
+ Raises :class:`ValueError` if ``tier`` is unknown (with the list of
247
+ valid tiers in the message) or if ``**kwargs`` contains a keyword
248
+ the selected pan doesn't accept (with the list of valid kwargs).
249
+ """
250
+ key = tier.lower().replace("_", "-")
251
+ if key not in TIERS:
252
+ raise ValueError(
253
+ f"unknown pan tier {tier!r}; valid tiers are: {sorted(TIERS)}"
254
+ )
255
+ cls = TIERS[key]
256
+ try:
257
+ return cls(source=source, **kwargs)
258
+ except TypeError as exc:
259
+ # Re-raise with a message that lists the keywords the tier
260
+ # actually accepts — the raw dataclass message ("unexpected
261
+ # keyword argument 'X'") doesn't say what *is* accepted.
262
+ import inspect
263
+ valid = [p for p in inspect.signature(cls).parameters if p != "source"]
264
+ raise ValueError(
265
+ f"{cls.__name__} rejected an argument ({exc}). "
266
+ f"Valid keyword arguments for {cls.__name__} (besides "
267
+ f"'source'): {valid}"
268
+ ) from exc
@@ -89,6 +89,112 @@ def test_pick_pan_factory() -> None:
89
89
  assert isinstance(p, pans.Pan)
90
90
 
91
91
 
92
+ # Regression: second positional arg used to silently bind to `name`
93
+ # (inherited from Pan.__init__), so ``Skillet(src, "instruct")`` would
94
+ # set ``name="instruct"`` and leave ``mode="chat"``. `name` is now a
95
+ # ClassVar, so positional arg #2 is the first real field on each pan.
96
+
97
+ def test_skillet_second_positional_is_mode_not_name() -> None:
98
+ from hypernix import pans
99
+
100
+ s = pans.Skillet(["hi"], "instruct")
101
+ assert s.mode == "instruct"
102
+ assert s.name == "Skillet" # unchanged — name is a ClassVar
103
+
104
+
105
+ def test_grill_pan_second_positional_is_min_chars() -> None:
106
+ from hypernix import pans
107
+
108
+ g = pans.GrillPan(["hello", "world", "hi", "hello"], 4)
109
+ assert g.min_chars == 4
110
+ # "hi" (2 chars) dropped; second "hello" dropped as duplicate.
111
+ assert list(g) == ["hello", "world"]
112
+
113
+
114
+ def test_grill_pan_seen_is_not_in_init_signature() -> None:
115
+ import inspect
116
+
117
+ from hypernix import pans
118
+
119
+ params = inspect.signature(pans.GrillPan).parameters
120
+ assert "_seen" not in params
121
+
122
+
123
+ def test_pick_pan_unknown_tier_gives_useful_error() -> None:
124
+ import pytest
125
+
126
+ from hypernix import pans
127
+
128
+ with pytest.raises(ValueError, match="unknown pan tier"):
129
+ pans.pick_pan("microwave-pan", source=["x"])
130
+
131
+
132
+ def test_pick_pan_bad_kwarg_lists_valid_ones() -> None:
133
+ import pytest
134
+
135
+ from hypernix import pans
136
+
137
+ with pytest.raises(ValueError, match="Skillet rejected"):
138
+ pans.pick_pan("skillet", source=["x"], min_chars=16)
139
+
140
+
141
+ # Regression: v0.45 shipped without a context_length / max_chars knob,
142
+ # so a training script calling ``FryingPan(context_length=256)`` got
143
+ # a bare TypeError. Every pan now accepts both as keyword-only fields.
144
+
145
+ def test_frying_pan_accepts_context_length() -> None:
146
+ from hypernix import pans
147
+
148
+ long_line = "x" * 10_000
149
+ out = list(pans.FryingPan(source=[long_line], context_length=256))
150
+ # context_length=256 → max_chars = 256 * 4 = 1024.
151
+ assert len(out[0]) == 1024
152
+
153
+
154
+ def test_pan_max_chars_truncates() -> None:
155
+ from hypernix import pans
156
+
157
+ long_line = "x" * 10_000
158
+ out = list(pans.SaucePan(source=[long_line], max_chars=500))
159
+ assert len(out[0]) == 500
160
+
161
+
162
+ def test_pan_context_length_wins_over_max_chars() -> None:
163
+ from hypernix import pans
164
+
165
+ long_line = "x" * 10_000
166
+ out = list(pans.FryingPan(
167
+ source=[long_line], max_chars=99, context_length=50,
168
+ ))
169
+ # ctx=50 → 50 * 4 = 200, not 99.
170
+ assert len(out[0]) == 200
171
+
172
+
173
+ def test_pan_length_cap_is_noop_when_unset() -> None:
174
+ from hypernix import pans
175
+
176
+ line = "x" * 10_000
177
+ out = list(pans.FryingPan(source=[line]))
178
+ assert len(out[0]) == 10_000
179
+
180
+
181
+ def test_wok_honors_context_length() -> None:
182
+ from hypernix import pans
183
+
184
+ lines = ["x" * 10_000, "y" * 10_000]
185
+ out = list(pans.Wok(source=lines, seed=0, context_length=64))
186
+ # 64 * 4 = 256
187
+ assert all(len(s) <= 256 for s in out)
188
+
189
+
190
+ def test_pick_pan_forwards_context_length() -> None:
191
+ from hypernix import pans
192
+
193
+ src = ["abcd " * 1000]
194
+ pan = pans.pick_pan("grill-pan", source=src, context_length=64)
195
+ assert all(len(s) <= 256 for s in pan)
196
+
197
+
92
198
  # ---------------------------------------------------------------------------
93
199
  # microwave
94
200
  # ---------------------------------------------------------------------------
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes