glitchlings 1.0.0__cp313-cp313-win_amd64.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.
- glitchlings/__init__.py +101 -0
- glitchlings/__main__.py +8 -0
- glitchlings/_corruption_engine/__init__.py +12 -0
- glitchlings/_corruption_engine.cp313-win_amd64.pyd +0 -0
- glitchlings/assets/__init__.py +180 -0
- glitchlings/assets/apostrofae_pairs.json +32 -0
- glitchlings/assets/ekkokin_homophones.json +2014 -0
- glitchlings/assets/hokey_assets.json +193 -0
- glitchlings/assets/lexemes/academic.json +1049 -0
- glitchlings/assets/lexemes/colors.json +1333 -0
- glitchlings/assets/lexemes/corporate.json +716 -0
- glitchlings/assets/lexemes/cyberpunk.json +22 -0
- glitchlings/assets/lexemes/lovecraftian.json +23 -0
- glitchlings/assets/lexemes/synonyms.json +3354 -0
- glitchlings/assets/mim1c_homoglyphs.json.gz.b64 +1064 -0
- glitchlings/assets/ocr_confusions.tsv +30 -0
- glitchlings/assets/pipeline_assets.json +29 -0
- glitchlings/attack/__init__.py +184 -0
- glitchlings/attack/analysis.py +1321 -0
- glitchlings/attack/core.py +819 -0
- glitchlings/attack/core_execution.py +378 -0
- glitchlings/attack/core_planning.py +612 -0
- glitchlings/attack/encode.py +114 -0
- glitchlings/attack/metrics.py +211 -0
- glitchlings/attack/metrics_dispatch.py +70 -0
- glitchlings/attack/tokenization.py +338 -0
- glitchlings/attack/tokenizer_metrics.py +373 -0
- glitchlings/auggie.py +285 -0
- glitchlings/compat/__init__.py +9 -0
- glitchlings/compat/loaders.py +355 -0
- glitchlings/compat/types.py +41 -0
- glitchlings/conf/__init__.py +39 -0
- glitchlings/conf/loaders.py +331 -0
- glitchlings/conf/schema.py +156 -0
- glitchlings/conf/types.py +72 -0
- glitchlings/config.toml +2 -0
- glitchlings/constants.py +139 -0
- glitchlings/dev/__init__.py +3 -0
- glitchlings/dev/docs.py +45 -0
- glitchlings/dlc/__init__.py +21 -0
- glitchlings/dlc/_shared.py +300 -0
- glitchlings/dlc/gutenberg.py +400 -0
- glitchlings/dlc/huggingface.py +68 -0
- glitchlings/dlc/langchain.py +147 -0
- glitchlings/dlc/nemo.py +283 -0
- glitchlings/dlc/prime.py +215 -0
- glitchlings/dlc/pytorch.py +98 -0
- glitchlings/dlc/pytorch_lightning.py +173 -0
- glitchlings/internal/__init__.py +16 -0
- glitchlings/internal/rust.py +159 -0
- glitchlings/internal/rust_ffi.py +599 -0
- glitchlings/main.py +426 -0
- glitchlings/protocols.py +91 -0
- glitchlings/runtime_config.py +24 -0
- glitchlings/util/__init__.py +41 -0
- glitchlings/util/adapters.py +65 -0
- glitchlings/util/keyboards.py +508 -0
- glitchlings/util/transcripts.py +108 -0
- glitchlings/zoo/__init__.py +161 -0
- glitchlings/zoo/assets/__init__.py +29 -0
- glitchlings/zoo/core.py +852 -0
- glitchlings/zoo/core_execution.py +154 -0
- glitchlings/zoo/core_planning.py +451 -0
- glitchlings/zoo/corrupt_dispatch.py +291 -0
- glitchlings/zoo/hokey.py +139 -0
- glitchlings/zoo/jargoyle.py +301 -0
- glitchlings/zoo/mim1c.py +269 -0
- glitchlings/zoo/pedant/__init__.py +109 -0
- glitchlings/zoo/pedant/core.py +99 -0
- glitchlings/zoo/pedant/forms.py +50 -0
- glitchlings/zoo/pedant/stones.py +83 -0
- glitchlings/zoo/redactyl.py +94 -0
- glitchlings/zoo/rng.py +280 -0
- glitchlings/zoo/rushmore.py +416 -0
- glitchlings/zoo/scannequin.py +370 -0
- glitchlings/zoo/transforms.py +331 -0
- glitchlings/zoo/typogre.py +194 -0
- glitchlings/zoo/validation.py +643 -0
- glitchlings/zoo/wherewolf.py +120 -0
- glitchlings/zoo/zeedub.py +165 -0
- glitchlings-1.0.0.dist-info/METADATA +404 -0
- glitchlings-1.0.0.dist-info/RECORD +86 -0
- glitchlings-1.0.0.dist-info/WHEEL +5 -0
- glitchlings-1.0.0.dist-info/entry_points.txt +3 -0
- glitchlings-1.0.0.dist-info/licenses/LICENSE +201 -0
- glitchlings-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
from glitchlings.constants import (
|
|
8
|
+
DEFAULT_TYPOGRE_KEYBOARD,
|
|
9
|
+
DEFAULT_TYPOGRE_MOTOR_WEIGHTING,
|
|
10
|
+
DEFAULT_TYPOGRE_RATE,
|
|
11
|
+
)
|
|
12
|
+
from glitchlings.internal.rust_ffi import keyboard_typo_rust, resolve_seed
|
|
13
|
+
|
|
14
|
+
from ..util import (
|
|
15
|
+
KEYNEIGHBORS,
|
|
16
|
+
MOTOR_WEIGHTS,
|
|
17
|
+
SHIFT_MAPS,
|
|
18
|
+
get_serialized_layout,
|
|
19
|
+
get_serialized_shift_map,
|
|
20
|
+
)
|
|
21
|
+
from .core import AttackOrder, AttackWave, Glitchling, PipelineOperationPayload
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _resolve_slip_exit_rate(
|
|
25
|
+
shift_slip_rate: float,
|
|
26
|
+
shift_slip_exit_rate: float | None,
|
|
27
|
+
) -> float:
|
|
28
|
+
"""Derive the slip exit rate, defaulting to a burst-friendly value."""
|
|
29
|
+
|
|
30
|
+
if shift_slip_exit_rate is not None:
|
|
31
|
+
return max(0.0, shift_slip_exit_rate)
|
|
32
|
+
return max(0.0, shift_slip_rate * 0.5)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _resolve_motor_weighting(motor_weighting: str | None) -> str:
|
|
36
|
+
"""Resolve motor weighting mode, validating against known modes."""
|
|
37
|
+
if motor_weighting is None:
|
|
38
|
+
return DEFAULT_TYPOGRE_MOTOR_WEIGHTING
|
|
39
|
+
|
|
40
|
+
normalized = motor_weighting.lower().replace("-", "_").replace(" ", "_")
|
|
41
|
+
if normalized not in MOTOR_WEIGHTS:
|
|
42
|
+
valid_modes = ", ".join(sorted(MOTOR_WEIGHTS.keys()))
|
|
43
|
+
message = f"Unknown motor weighting '{motor_weighting}'. Valid modes: {valid_modes}"
|
|
44
|
+
raise ValueError(message)
|
|
45
|
+
return normalized
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def fatfinger(
|
|
49
|
+
text: str,
|
|
50
|
+
rate: float | None = None,
|
|
51
|
+
keyboard: str = DEFAULT_TYPOGRE_KEYBOARD,
|
|
52
|
+
layout: Mapping[str, Sequence[str]] | None = None,
|
|
53
|
+
seed: int | None = None,
|
|
54
|
+
rng: random.Random | None = None,
|
|
55
|
+
*,
|
|
56
|
+
shift_slip_rate: float = 0.0,
|
|
57
|
+
shift_slip_exit_rate: float | None = None,
|
|
58
|
+
shift_map: Mapping[str, str] | None = None,
|
|
59
|
+
motor_weighting: str | None = None,
|
|
60
|
+
) -> str:
|
|
61
|
+
"""Introduce character-level "fat finger" edits with a Rust fast path.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
text: Input text to corrupt.
|
|
65
|
+
rate: Probability of corrupting each character (default 0.02).
|
|
66
|
+
keyboard: Keyboard layout name for adjacency mapping.
|
|
67
|
+
layout: Custom keyboard neighbor mapping (overrides keyboard).
|
|
68
|
+
seed: Deterministic seed for reproducible results.
|
|
69
|
+
rng: Random generator (alternative to seed).
|
|
70
|
+
shift_slip_rate: Probability of entering a shifted burst.
|
|
71
|
+
shift_slip_exit_rate: Probability of releasing shift during a burst.
|
|
72
|
+
shift_map: Custom unshifted->shifted character mapping.
|
|
73
|
+
motor_weighting: Weighting mode for error sampling based on finger/hand
|
|
74
|
+
coordination. One of 'uniform' (default), 'wet_ink', or 'hastily_edited'.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Text with simulated typing errors.
|
|
78
|
+
"""
|
|
79
|
+
effective_rate = DEFAULT_TYPOGRE_RATE if rate is None else rate
|
|
80
|
+
|
|
81
|
+
if not text:
|
|
82
|
+
return ""
|
|
83
|
+
|
|
84
|
+
layout_mapping = layout if layout is not None else getattr(KEYNEIGHBORS, keyboard)
|
|
85
|
+
slip_rate = max(0.0, shift_slip_rate)
|
|
86
|
+
slip_exit_rate = _resolve_slip_exit_rate(slip_rate, shift_slip_exit_rate)
|
|
87
|
+
slip_map = shift_map if shift_map is not None else getattr(SHIFT_MAPS, keyboard, None)
|
|
88
|
+
resolved_motor_weighting = _resolve_motor_weighting(motor_weighting)
|
|
89
|
+
|
|
90
|
+
clamped_rate = max(0.0, effective_rate)
|
|
91
|
+
if slip_rate == 0.0 and clamped_rate == 0.0:
|
|
92
|
+
return text
|
|
93
|
+
|
|
94
|
+
return keyboard_typo_rust(
|
|
95
|
+
text,
|
|
96
|
+
clamped_rate,
|
|
97
|
+
layout_mapping,
|
|
98
|
+
resolve_seed(seed, rng),
|
|
99
|
+
shift_slip_rate=slip_rate,
|
|
100
|
+
shift_slip_exit_rate=slip_exit_rate,
|
|
101
|
+
shift_map=slip_map,
|
|
102
|
+
motor_weighting=resolved_motor_weighting,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class Typogre(Glitchling):
|
|
107
|
+
"""Glitchling that introduces deterministic keyboard-typing errors.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
rate: Probability of corrupting each character (default 0.02).
|
|
111
|
+
keyboard: Keyboard layout name for adjacency mapping.
|
|
112
|
+
shift_slip_rate: Probability of entering a shifted burst.
|
|
113
|
+
shift_slip_exit_rate: Probability of releasing shift during a burst.
|
|
114
|
+
motor_weighting: Weighting mode for error sampling based on finger/hand
|
|
115
|
+
coordination. One of:
|
|
116
|
+
- 'uniform': All neighbors equally likely (default, original behavior).
|
|
117
|
+
- 'wet_ink': Simulates uncorrected errors - same-finger errors are
|
|
118
|
+
caught and corrected, cross-hand errors slip through.
|
|
119
|
+
- 'hastily_edited': Simulates raw typing before correction - same-finger
|
|
120
|
+
errors occur most often.
|
|
121
|
+
seed: Deterministic seed for reproducible results.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
flavor = "What a nice word, would be a shame if something happened to it..."
|
|
125
|
+
|
|
126
|
+
def __init__(
|
|
127
|
+
self,
|
|
128
|
+
*,
|
|
129
|
+
rate: float | None = None,
|
|
130
|
+
keyboard: str = DEFAULT_TYPOGRE_KEYBOARD,
|
|
131
|
+
shift_slip_rate: float = 0.0,
|
|
132
|
+
shift_slip_exit_rate: float | None = None,
|
|
133
|
+
motor_weighting: str | None = None,
|
|
134
|
+
seed: int | None = None,
|
|
135
|
+
**kwargs: Any,
|
|
136
|
+
) -> None:
|
|
137
|
+
effective_rate = DEFAULT_TYPOGRE_RATE if rate is None else rate
|
|
138
|
+
resolved_motor_weighting = _resolve_motor_weighting(motor_weighting)
|
|
139
|
+
super().__init__(
|
|
140
|
+
name="Typogre",
|
|
141
|
+
corruption_function=fatfinger,
|
|
142
|
+
scope=AttackWave.CHARACTER,
|
|
143
|
+
order=AttackOrder.EARLY,
|
|
144
|
+
seed=seed,
|
|
145
|
+
rate=effective_rate,
|
|
146
|
+
keyboard=keyboard,
|
|
147
|
+
shift_slip_rate=max(0.0, shift_slip_rate),
|
|
148
|
+
shift_slip_exit_rate=shift_slip_exit_rate,
|
|
149
|
+
motor_weighting=resolved_motor_weighting,
|
|
150
|
+
**kwargs,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def pipeline_operation(self) -> PipelineOperationPayload:
|
|
154
|
+
rate_value = self.kwargs.get("rate")
|
|
155
|
+
rate = DEFAULT_TYPOGRE_RATE if rate_value is None else float(rate_value)
|
|
156
|
+
keyboard = str(self.kwargs.get("keyboard", DEFAULT_TYPOGRE_KEYBOARD))
|
|
157
|
+
|
|
158
|
+
# Use pre-serialized layout (cached at module load time)
|
|
159
|
+
serialized_layout = get_serialized_layout(keyboard)
|
|
160
|
+
if serialized_layout is None:
|
|
161
|
+
message = f"Unknown keyboard layout '{keyboard}' for Typogre pipeline"
|
|
162
|
+
raise RuntimeError(message)
|
|
163
|
+
|
|
164
|
+
shift_slip_rate = float(self.kwargs.get("shift_slip_rate", 0.0) or 0.0)
|
|
165
|
+
shift_slip_exit_rate = self.kwargs.get("shift_slip_exit_rate")
|
|
166
|
+
resolved_exit_rate = _resolve_slip_exit_rate(shift_slip_rate, shift_slip_exit_rate)
|
|
167
|
+
|
|
168
|
+
# Use pre-serialized shift map (already a dict, no copy needed)
|
|
169
|
+
serialized_shift_map = get_serialized_shift_map(keyboard)
|
|
170
|
+
if shift_slip_rate > 0.0 and serialized_shift_map is None:
|
|
171
|
+
message = f"Unknown shift map layout '{keyboard}' for Typogre pipeline"
|
|
172
|
+
raise RuntimeError(message)
|
|
173
|
+
|
|
174
|
+
motor_weighting = self.kwargs.get("motor_weighting", DEFAULT_TYPOGRE_MOTOR_WEIGHTING)
|
|
175
|
+
|
|
176
|
+
return cast(
|
|
177
|
+
PipelineOperationPayload,
|
|
178
|
+
{
|
|
179
|
+
"type": "typo",
|
|
180
|
+
"rate": float(rate),
|
|
181
|
+
"keyboard": keyboard,
|
|
182
|
+
"layout": serialized_layout,
|
|
183
|
+
"shift_slip_rate": shift_slip_rate,
|
|
184
|
+
"shift_slip_exit_rate": float(resolved_exit_rate),
|
|
185
|
+
"shift_map": serialized_shift_map,
|
|
186
|
+
"motor_weighting": str(motor_weighting),
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
typogre = Typogre()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
__all__ = ["Typogre", "typogre", "fatfinger"]
|