glitchlings 0.4.4__cp310-cp310-macosx_11_0_universal2.whl → 0.4.5__cp310-cp310-macosx_11_0_universal2.whl

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.

Potentially problematic release.


This version of glitchlings might be problematic. Click here for more details.

glitchlings/__init__.py CHANGED
@@ -5,6 +5,7 @@ from .zoo import (
5
5
  Apostrofae,
6
6
  Gaggle,
7
7
  Glitchling,
8
+ Hokey,
8
9
  Jargoyle,
9
10
  Mim1c,
10
11
  Redactyl,
@@ -15,6 +16,7 @@ from .zoo import (
15
16
  Zeedub,
16
17
  adjax,
17
18
  apostrofae,
19
+ hokey,
18
20
  is_rust_pipeline_enabled,
19
21
  is_rust_pipeline_supported,
20
22
  jargoyle,
@@ -42,6 +44,8 @@ __all__ = [
42
44
  "adjax",
43
45
  "Apostrofae",
44
46
  "apostrofae",
47
+ "Hokey",
48
+ "hokey",
45
49
  "Redactyl",
46
50
  "redactyl",
47
51
  "Reduple",
Binary file
glitchlings/compat.py CHANGED
@@ -17,16 +17,14 @@ _MISSING = _MissingSentinel()
17
17
 
18
18
 
19
19
  class _MarkerProtocol(Protocol):
20
- def evaluate(self, environment: dict[str, str]) -> bool:
21
- ...
20
+ def evaluate(self, environment: dict[str, str]) -> bool: ...
22
21
 
23
22
 
24
23
  class _RequirementProtocol(Protocol):
25
24
  marker: _MarkerProtocol | None
26
25
  name: str
27
26
 
28
- def __init__(self, requirement: str) -> None:
29
- ...
27
+ def __init__(self, requirement: str) -> None: ...
30
28
 
31
29
 
32
30
  try: # pragma: no cover - packaging is bundled with modern Python environments
glitchlings/config.py CHANGED
@@ -19,8 +19,7 @@ except ModuleNotFoundError: # pragma: no cover - Python < 3.11
19
19
 
20
20
 
21
21
  class _TomllibModule(Protocol):
22
- def load(self, fp: IO[bytes]) -> Any:
23
- ...
22
+ def load(self, fp: IO[bytes]) -> Any: ...
24
23
 
25
24
 
26
25
  tomllib = cast(_TomllibModule, _tomllib)
@@ -29,8 +28,7 @@ tomllib = cast(_TomllibModule, _tomllib)
29
28
  class _YamlModule(Protocol):
30
29
  YAMLError: type[Exception]
31
30
 
32
- def safe_load(self, stream: str) -> Any:
33
- ...
31
+ def safe_load(self, stream: str) -> Any: ...
34
32
 
35
33
 
36
34
  yaml = cast(_YamlModule, importlib.import_module("yaml"))
@@ -0,0 +1 @@
1
+ """Static data assets shared across Glitchlings implementations."""
@@ -0,0 +1,193 @@
1
+ {
2
+ "lexical_prior": {
3
+ "so": 0.92,
4
+ "no": 0.89,
5
+ "go": 0.72,
6
+ "yeah": 0.86,
7
+ "yay": 0.81,
8
+ "ya": 0.7,
9
+ "hey": 0.66,
10
+ "okay": 0.68,
11
+ "ok": 0.64,
12
+ "cool": 0.78,
13
+ "omg": 0.74,
14
+ "wow": 0.88,
15
+ "wee": 0.62,
16
+ "woo": 0.69,
17
+ "woohoo": 0.74,
18
+ "whoa": 0.71,
19
+ "woah": 0.7,
20
+ "yayyy": 0.75,
21
+ "yayyyy": 0.76,
22
+ "yas": 0.79,
23
+ "yass": 0.8,
24
+ "yaaas": 0.82,
25
+ "please": 0.53,
26
+ "pleaseee": 0.57,
27
+ "pleaseeee": 0.6,
28
+ "pleaseeeee": 0.63,
29
+ "lol": 0.83,
30
+ "lmao": 0.65,
31
+ "omggg": 0.75,
32
+ "omgggg": 0.76,
33
+ "squee": 0.64,
34
+ "hahaha": 0.6,
35
+ "haha": 0.56,
36
+ "really": 0.58,
37
+ "very": 0.49,
38
+ "love": 0.55,
39
+ "cute": 0.52,
40
+ "nice": 0.47,
41
+ "sweet": 0.45,
42
+ "yayness": 0.44,
43
+ "ugh": 0.5,
44
+ "aww": 0.61,
45
+ "yess": 0.81,
46
+ "yes": 0.9,
47
+ "pls": 0.48,
48
+ "pleeeease": 0.62,
49
+ "nooo": 0.88,
50
+ "noooo": 0.89,
51
+ "dang": 0.41,
52
+ "geez": 0.39,
53
+ "danggg": 0.44,
54
+ "dangit": 0.38,
55
+ "sick": 0.35,
56
+ "epic": 0.37,
57
+ "rad": 0.5,
58
+ "goal": 0.56,
59
+ "great": 0.46,
60
+ "awesome": 0.51,
61
+ "amazing": 0.52,
62
+ "perfect": 0.49,
63
+ "fantastic": 0.5,
64
+ "stellar": 0.48,
65
+ "yippee": 0.67,
66
+ "stoked": 0.48,
67
+ "yikes": 0.43,
68
+ "gosh": 0.41,
69
+ "heck": 0.36
70
+ },
71
+ "interjections": [
72
+ "wow",
73
+ "omg",
74
+ "hey",
75
+ "ugh",
76
+ "yay",
77
+ "yayyy",
78
+ "yayyyy",
79
+ "woo",
80
+ "woohoo",
81
+ "whoa",
82
+ "woah",
83
+ "whooo",
84
+ "ah",
85
+ "aw",
86
+ "aww",
87
+ "hmm",
88
+ "huh",
89
+ "yo",
90
+ "yikes",
91
+ "gah",
92
+ "phew",
93
+ "sheesh"
94
+ ],
95
+ "intensifiers": [
96
+ "so",
97
+ "very",
98
+ "really",
99
+ "super",
100
+ "mega",
101
+ "ultra",
102
+ "too",
103
+ "way",
104
+ "crazy",
105
+ "insanely",
106
+ "totally",
107
+ "extremely",
108
+ "seriously",
109
+ "absolutely",
110
+ "completely",
111
+ "entirely",
112
+ "utterly",
113
+ "hella",
114
+ "wicked",
115
+ "truly"
116
+ ],
117
+ "evaluatives": [
118
+ "cool",
119
+ "great",
120
+ "awesome",
121
+ "amazing",
122
+ "perfect",
123
+ "nice",
124
+ "sweet",
125
+ "lovely",
126
+ "loving",
127
+ "silly",
128
+ "wild",
129
+ "fun",
130
+ "funny",
131
+ "adorable",
132
+ "cute",
133
+ "fantastic",
134
+ "fabulous",
135
+ "brilliant",
136
+ "stellar",
137
+ "rad",
138
+ "epic",
139
+ "delightful",
140
+ "gorgeous"
141
+ ],
142
+ "positive_lexicon": [
143
+ "love",
144
+ "loved",
145
+ "loving",
146
+ "like",
147
+ "liked",
148
+ "awesome",
149
+ "amazing",
150
+ "yay",
151
+ "great",
152
+ "good",
153
+ "fun",
154
+ "funny",
155
+ "blessed",
156
+ "excited",
157
+ "cool",
158
+ "best",
159
+ "beautiful",
160
+ "happy",
161
+ "happiest",
162
+ "joy",
163
+ "joyful",
164
+ "thrilled",
165
+ "ecstatic",
166
+ "stoked",
167
+ "pumped",
168
+ "glad"
169
+ ],
170
+ "negative_lexicon": [
171
+ "bad",
172
+ "sad",
173
+ "angry",
174
+ "annoyed",
175
+ "mad",
176
+ "terrible",
177
+ "awful",
178
+ "hate",
179
+ "hated",
180
+ "crying",
181
+ "hurt",
182
+ "tired",
183
+ "worst",
184
+ "ugh",
185
+ "nope",
186
+ "upset",
187
+ "frustrated",
188
+ "drained",
189
+ "exhausted",
190
+ "bummed",
191
+ "grumpy"
192
+ ]
193
+ }
@@ -67,10 +67,10 @@ def resolve_columns(dataset: Any, columns: Sequence[str] | None) -> list[str]:
67
67
  raise ValueError("Unable to determine which dataset columns to corrupt.")
68
68
 
69
69
 
70
- def normalise_column_spec(
70
+ def normalize_column_spec(
71
71
  columns: str | int | Sequence[str | int] | None,
72
72
  ) -> list[str | int] | None:
73
- """Normalise a column specification into a list of keys or indices.
73
+ """Normalize a column specification into a list of keys or indices.
74
74
 
75
75
  Args:
76
76
  columns: Column specification as a single value, sequence of values, or None.
@@ -87,10 +87,10 @@ def normalise_column_spec(
87
87
  if isinstance(columns, (str, int)):
88
88
  return [columns]
89
89
 
90
- normalised = list(columns)
91
- if not normalised:
90
+ normalized = list(columns)
91
+ if not normalized:
92
92
  raise ValueError("At least one column must be specified")
93
- return normalised
93
+ return normalized
94
94
 
95
95
 
96
96
  def is_textual_candidate(value: Any) -> bool:
@@ -147,7 +147,7 @@ def corrupt_text_value(value: Any, gaggle: Gaggle) -> Any:
147
147
  __all__ = [
148
148
  "corrupt_text_value",
149
149
  "is_textual_candidate",
150
- "normalise_column_spec",
150
+ "normalize_column_spec",
151
151
  "resolve_columns",
152
152
  "resolve_environment",
153
153
  ]
@@ -10,15 +10,15 @@ from ..util.adapters import coerce_gaggle
10
10
  from ..zoo import Gaggle, Glitchling
11
11
 
12
12
 
13
- def _normalise_columns(column: str | Sequence[str]) -> list[str]:
14
- """Normalise a column specification to a list."""
13
+ def _normalize_columns(column: str | Sequence[str]) -> list[str]:
14
+ """Normalize a column specification to a list."""
15
15
  if isinstance(column, str):
16
16
  return [column]
17
17
 
18
- normalised = list(column)
19
- if not normalised:
18
+ normalized = list(column)
19
+ if not normalized:
20
20
  raise ValueError("At least one column must be specified")
21
- return normalised
21
+ return normalized
22
22
 
23
23
 
24
24
  def _glitch_dataset(
@@ -29,7 +29,7 @@ def _glitch_dataset(
29
29
  seed: int = 151,
30
30
  ) -> Any:
31
31
  """Apply glitchlings to the provided dataset columns."""
32
- columns = _normalise_columns(column)
32
+ columns = _normalize_columns(column)
33
33
  gaggle = coerce_gaggle(glitchlings, seed=seed)
34
34
  return gaggle.corrupt_dataset(dataset, columns)
35
35
 
glitchlings/dlc/prime.py CHANGED
@@ -117,7 +117,7 @@ def _as_gaggle(
117
117
 
118
118
 
119
119
  def _extract_completion_text(completion: Any) -> str:
120
- """Normalise a completion payload into a plain string."""
120
+ """Normalize a completion payload into a plain string."""
121
121
  if isinstance(completion, str):
122
122
  return completion
123
123
 
@@ -9,7 +9,7 @@ from ..compat import get_torch_dataloader, require_torch
9
9
  from ..compat import torch as _torch_dependency
10
10
  from ..util.adapters import coerce_gaggle
11
11
  from ..zoo import Gaggle, Glitchling
12
- from ._shared import corrupt_text_value, is_textual_candidate, normalise_column_spec
12
+ from ._shared import corrupt_text_value, is_textual_candidate, normalize_column_spec
13
13
 
14
14
 
15
15
  def _apply_to_batch(batch: Any, targets: list[str | int] | None, gaggle: Gaggle) -> Any:
@@ -134,8 +134,8 @@ def _ensure_dataloader_class() -> type[Any]:
134
134
  ) -> _GlitchedDataLoader:
135
135
  """Return a lazily glitched view of the loader's batches."""
136
136
  gaggle = coerce_gaggle(glitchlings, seed=seed)
137
- normalised = normalise_column_spec(columns)
138
- return _GlitchedDataLoader(self, gaggle, columns=normalised)
137
+ normalized = normalize_column_spec(columns)
138
+ return _GlitchedDataLoader(self, gaggle, columns=normalized)
139
139
 
140
140
  setattr(dataloader_cls, "glitch", glitch)
141
141
 
@@ -8,7 +8,7 @@ from typing import Any, cast
8
8
  from ..compat import get_pytorch_lightning_datamodule, require_pytorch_lightning
9
9
  from ..util.adapters import coerce_gaggle
10
10
  from ..zoo import Gaggle, Glitchling
11
- from ._shared import corrupt_text_value, normalise_column_spec
11
+ from ._shared import corrupt_text_value, normalize_column_spec
12
12
 
13
13
 
14
14
  def _glitch_batch(batch: Any, columns: list[str], gaggle: Gaggle) -> Any:
@@ -40,10 +40,7 @@ def _wrap_dataloader(dataloader: Any, columns: list[str], gaggle: Gaggle) -> Any
40
40
  if isinstance(dataloader, Mapping):
41
41
  mapping_type = cast(type[Any], dataloader.__class__)
42
42
  return mapping_type(
43
- {
44
- key: _wrap_dataloader(value, columns, gaggle)
45
- for key, value in dataloader.items()
46
- }
43
+ {key: _wrap_dataloader(value, columns, gaggle) for key, value in dataloader.items()}
47
44
  )
48
45
 
49
46
  if isinstance(dataloader, list):
@@ -54,9 +51,7 @@ def _wrap_dataloader(dataloader: Any, columns: list[str], gaggle: Gaggle) -> Any
54
51
 
55
52
  if isinstance(dataloader, Sequence) and not isinstance(dataloader, (str, bytes, bytearray)):
56
53
  sequence_type = cast(type[Any], dataloader.__class__)
57
- return sequence_type(
58
- _wrap_dataloader(value, columns, gaggle) for value in dataloader
59
- )
54
+ return sequence_type(_wrap_dataloader(value, columns, gaggle) for value in dataloader)
60
55
 
61
56
  return _GlitchedDataLoader(dataloader, columns, gaggle)
62
57
 
@@ -89,7 +84,7 @@ def _glitch_datamodule(
89
84
  ) -> Any:
90
85
  """Return a proxy that applies glitchlings to batches from the datamodule."""
91
86
 
92
- columns = normalise_column_spec(column)
87
+ columns = normalize_column_spec(column)
93
88
  if columns is None: # pragma: no cover - defensive
94
89
  raise ValueError("At least one column must be specified")
95
90
  # Lightning datamodules only support string column names (mapping keys)
@@ -212,4 +207,3 @@ else: # pragma: no cover - optional dependency
212
207
 
213
208
 
214
209
  __all__ = ["LightningDataModule", "install"]
215
-
@@ -19,7 +19,7 @@ class CacheSnapshot:
19
19
  checksum: str | None = None
20
20
 
21
21
 
22
- def _normalise_entries(payload: Mapping[str, object]) -> CacheEntries:
22
+ def _normalize_entries(payload: Mapping[str, object]) -> CacheEntries:
23
23
  """Convert raw cache payloads into canonical mapping form."""
24
24
  entries: CacheEntries = {}
25
25
  for key, values in payload.items():
@@ -75,7 +75,7 @@ def load_cache(path: Path) -> CacheSnapshot:
75
75
  else:
76
76
  entries_payload = payload # legacy format without metadata
77
77
 
78
- entries = _normalise_entries(entries_payload)
78
+ entries = _normalize_entries(entries_payload)
79
79
  if checksum is not None:
80
80
  expected = compute_checksum(entries)
81
81
  if checksum != expected:
@@ -88,9 +88,7 @@ def load_cache(path: Path) -> CacheSnapshot:
88
88
 
89
89
  def write_cache(path: Path, entries: Mapping[str, Sequence[str]]) -> CacheSnapshot:
90
90
  """Persist ``entries`` to ``path`` with checksum metadata."""
91
- serialisable: CacheEntries = {
92
- key: list(values) for key, values in sorted(entries.items())
93
- }
91
+ serialisable: CacheEntries = {key: list(values) for key, values in sorted(entries.items())}
94
92
  checksum = compute_checksum(serialisable)
95
93
  payload = {
96
94
  "__meta__": {
@@ -16,6 +16,9 @@ from ._cache import CacheSnapshot
16
16
  from ._cache import load_cache as _load_cache_file
17
17
  from ._cache import write_cache as _write_cache_file
18
18
 
19
+ # Minimum number of neighbors to consider for similarity queries
20
+ MIN_NEIGHBORS = 1
21
+
19
22
 
20
23
  def _cosine_similarity(vector_a: Sequence[float], vector_b: Sequence[float]) -> float:
21
24
  """Return the cosine similarity between two dense vectors."""
@@ -304,7 +307,7 @@ class VectorLexicon(LexiconBackend):
304
307
  """Initialise the lexicon with an embedding ``source`` and optional cache."""
305
308
  super().__init__(seed=seed)
306
309
  self._adapter = _resolve_source(source)
307
- self._max_neighbors = max(1, max_neighbors)
310
+ self._max_neighbors = max(MIN_NEIGHBORS, max_neighbors)
308
311
  self._min_similarity = min_similarity
309
312
  self._cache: MutableMapping[str, list[str]] = {}
310
313
  self._cache_path: Path | None
@@ -371,7 +374,7 @@ class VectorLexicon(LexiconBackend):
371
374
  if cache_key in self._cache:
372
375
  return self._cache[cache_key]
373
376
 
374
- neighbor_limit = self._max_neighbors if limit is None else max(1, limit)
377
+ neighbor_limit = self._max_neighbors if limit is None else max(MIN_NEIGHBORS, limit)
375
378
  neighbors = self._fetch_neighbors(
376
379
  original=original, normalized=normalized, limit=neighbor_limit
377
380
  )
@@ -624,9 +627,7 @@ def main(argv: Sequence[str] | None = None) -> int:
624
627
  )
625
628
  iterator = lexicon.iter_vocabulary()
626
629
  if args.limit is not None:
627
- token_iter = (
628
- token for index, token in enumerate(iterator) if index < args.limit
629
- )
630
+ token_iter = (token for index, token in enumerate(iterator) if index < args.limit)
630
631
  else:
631
632
  token_iter = iterator
632
633
 
@@ -13,21 +13,17 @@ from ._cache import CacheSnapshot
13
13
 
14
14
 
15
15
  class _LemmaProtocol(Protocol):
16
- def name(self) -> str:
17
- ...
16
+ def name(self) -> str: ...
18
17
 
19
18
 
20
19
  class _SynsetProtocol(Protocol):
21
- def lemmas(self) -> Sequence[_LemmaProtocol]:
22
- ...
20
+ def lemmas(self) -> Sequence[_LemmaProtocol]: ...
23
21
 
24
22
 
25
23
  class _WordNetResource(Protocol):
26
- def synsets(self, word: str, pos: str | None = None) -> Sequence[_SynsetProtocol]:
27
- ...
24
+ def synsets(self, word: str, pos: str | None = None) -> Sequence[_SynsetProtocol]: ...
28
25
 
29
- def ensure_loaded(self) -> None:
30
- ...
26
+ def ensure_loaded(self) -> None: ...
31
27
 
32
28
 
33
29
  WordNetCorpusReaderFactory = Callable[[Any, Any], _WordNetResource]
@@ -0,0 +1,144 @@
1
+ """Hokey expressive lengthening generator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from .stretch_locator import StretchSite, apply_stretch, find_stretch_site
8
+ from .stretchability import RandomLike, StretchabilityAnalyzer, StretchabilityFeatures
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class HokeyConfig:
13
+ rate: float = 0.3
14
+ extension_min: int = 2
15
+ extension_max: int = 5
16
+ base_p: float = 0.45
17
+ word_length_threshold: int = 6
18
+
19
+
20
+ @dataclass(slots=True)
21
+ class StretchEvent:
22
+ token_index: int
23
+ original: str
24
+ stretched: str
25
+ repeats: int
26
+ site: StretchSite
27
+ score: float
28
+ features: StretchabilityFeatures
29
+
30
+
31
+ class NegativeBinomialSampler:
32
+ """Sample stretch lengths from a clipped negative binomial distribution."""
33
+
34
+ def __init__(self, base_p: float = 0.45) -> None:
35
+ self.base_p = base_p
36
+
37
+ def sample(
38
+ self,
39
+ rng: RandomLike,
40
+ *,
41
+ intensity: float,
42
+ minimum: int,
43
+ maximum: int,
44
+ ) -> int:
45
+ minimum = max(0, int(minimum))
46
+ maximum = max(minimum, int(maximum))
47
+ if maximum == 0:
48
+ return 0
49
+ if maximum == minimum:
50
+ return maximum
51
+
52
+ r = max(1, int(round(1 + 2 * intensity)))
53
+ adjusted_p = self.base_p / (1.0 + 0.75 * max(0.0, intensity))
54
+ adjusted_p = max(0.05, min(0.95, adjusted_p))
55
+ failures = sum(self._geometric_sample(rng, adjusted_p) for _ in range(r))
56
+ extra = minimum + failures
57
+ return max(minimum, min(maximum, extra))
58
+
59
+ @staticmethod
60
+ def _geometric_sample(rng: RandomLike, p: float) -> int:
61
+ count = 0
62
+ while rng.random() > p:
63
+ count += 1
64
+ return count
65
+
66
+
67
+ class HokeyGenerator:
68
+ """Full expressive lengthening pipeline."""
69
+
70
+ def __init__(
71
+ self,
72
+ analyzer: StretchabilityAnalyzer | None = None,
73
+ sampler: NegativeBinomialSampler | None = None,
74
+ ) -> None:
75
+ self.analyzer = analyzer or StretchabilityAnalyzer()
76
+ self.sampler = sampler or NegativeBinomialSampler()
77
+
78
+ def generate(
79
+ self,
80
+ text: str,
81
+ *,
82
+ rng: RandomLike,
83
+ config: HokeyConfig,
84
+ ) -> tuple[str, list[StretchEvent]]:
85
+ if not text:
86
+ return text, []
87
+
88
+ if config.base_p != self.sampler.base_p:
89
+ self.sampler.base_p = config.base_p
90
+
91
+ tokens = self.analyzer.tokenise(text)
92
+ candidates = self.analyzer.analyse_tokens(tokens)
93
+ selected = self.analyzer.select_candidates(candidates, rate=config.rate, rng=rng)
94
+ if not selected:
95
+ return text, []
96
+
97
+ token_strings = [token.text for token in tokens]
98
+ events: list[StretchEvent] = []
99
+
100
+ for candidate in selected:
101
+ token_idx = candidate.token.index
102
+ original = token_strings[token_idx]
103
+ site = find_stretch_site(original)
104
+ if site is None:
105
+ continue
106
+
107
+ intensity = min(1.5, candidate.features.intensity() + 0.35 * candidate.score)
108
+ alpha_count = sum(1 for ch in original if ch.isalpha())
109
+ if config.word_length_threshold > 0 and alpha_count > config.word_length_threshold * 2:
110
+ continue
111
+ if config.word_length_threshold > 0 and alpha_count > config.word_length_threshold:
112
+ excess = alpha_count - config.word_length_threshold
113
+ intensity = intensity / (1.0 + 0.35 * excess)
114
+ if candidate.score < 0.35 and excess >= 2:
115
+ continue
116
+ intensity = max(0.05, intensity)
117
+
118
+ repeats = self.sampler.sample(
119
+ rng,
120
+ intensity=intensity,
121
+ minimum=config.extension_min,
122
+ maximum=config.extension_max,
123
+ )
124
+ if repeats <= 0:
125
+ continue
126
+
127
+ stretched_word = apply_stretch(original, site, repeats)
128
+ token_strings[token_idx] = stretched_word
129
+ events.append(
130
+ StretchEvent(
131
+ token_index=token_idx,
132
+ original=original,
133
+ stretched=stretched_word,
134
+ repeats=repeats,
135
+ site=site,
136
+ score=candidate.score,
137
+ features=candidate.features,
138
+ )
139
+ )
140
+
141
+ return "".join(token_strings), events
142
+
143
+
144
+ __all__ = ["HokeyGenerator", "HokeyConfig", "StretchEvent", "NegativeBinomialSampler"]