sonatoki 0.1.0__tar.gz → 0.1.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.
- {sonatoki-0.1.0 → sonatoki-0.1.1}/PKG-INFO +29 -6
- {sonatoki-0.1.0 → sonatoki-0.1.1}/README.md +28 -5
- {sonatoki-0.1.0 → sonatoki-0.1.1}/pyproject.toml +1 -1
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/Scorers.py +16 -16
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/ilo.py +30 -11
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_ilo.py +27 -7
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/tokenize_cases/tokenize_words_tok.yml +15 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/LICENSE +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/Cleaners.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/Filters.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/Preprocessors.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/Tokenizers.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/__init__.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/__main__.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/constants.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/src/sonatoki/linku.json +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/__init__.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_cleaners.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_filters.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_preprocessors.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_scorers.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_tokenize.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/test_utils.py +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/tokenize_cases/tokenize_sentences.yml +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/tokenize_cases/tokenize_sentences_tok.yml +0 -0
- {sonatoki-0.1.0 → sonatoki-0.1.1}/tests/tokenize_cases/tokenize_words.yml +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sonatoki
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.1
|
4
4
|
Summary: ilo li moku e toki li pana e sona ni: ni li toki ala toki pona?
|
5
5
|
Author-Email: "jan Kekan San (@gregdan3)" <gregory.danielson3@gmail.com>
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -44,7 +44,7 @@ from sonatoki.Filters import (
|
|
44
44
|
ProperName,
|
45
45
|
Punctuations,
|
46
46
|
)
|
47
|
-
from sonatoki.Scorers import
|
47
|
+
from sonatoki.Scorers import SoftScaling
|
48
48
|
from sonatoki.Cleaners import ConsecutiveDuplicates
|
49
49
|
from sonatoki.Tokenizers import word_tokenize_tok
|
50
50
|
from sonatoki.Preprocessors import URLs, DiscordEmotes
|
@@ -55,22 +55,23 @@ def main():
|
|
55
55
|
ignoring_filters=[Numerics, Punctuations],
|
56
56
|
scoring_filters=[NimiLinku, Syllabic, ProperName, Alphabetic],
|
57
57
|
cleaners=[ConsecutiveDuplicates],
|
58
|
-
scorer=
|
58
|
+
scorer=SoftScaling,
|
59
59
|
tokenizer=word_tokenize_tok,
|
60
60
|
)
|
61
61
|
ilo.is_toki_pona("imagine how is touch the sky") # False
|
62
62
|
ilo.is_toki_pona("o pilin insa e ni: sina pilin e sewi") # True
|
63
|
+
ilo.is_toki_pona("I Think I Can Evade Detection") # False
|
63
64
|
|
64
65
|
if __name__ == "__main__":
|
65
66
|
main()
|
66
67
|
```
|
67
68
|
|
68
|
-
`Ilo` is highly configurable by design, so I recommend exploring the `Preprocessors`, `Filters`, and `Scorers` modules. The `Cleaners` module only contains one cleaner, which I
|
69
|
+
`Ilo` is highly configurable by design, so I recommend exploring the `Preprocessors`, `Filters`, and `Scorers` modules. The `Cleaners` module only contains one cleaner, which I recommend using. The `Tokenizers` module contains several other word tokenizers, but their performance will be worse than the dedicated Toki Pona tokenizer `word_tokenize_tok`.
|
69
70
|
|
70
71
|
## Development
|
71
72
|
|
72
73
|
1. Install [pdm](https://github.com/pdm-project/pdm)
|
73
|
-
1. `pdm
|
74
|
+
1. `pdm install --dev`
|
74
75
|
1. Open any file you like!
|
75
76
|
|
76
77
|
## FAQ
|
@@ -81,4 +82,26 @@ The intent is to show our methodology to the Unicode Consortium, particularly to
|
|
81
82
|
|
82
83
|
After our proposal has been examined and a result given by the committee, I will translate this file and library into Toki Pona, with a note left behind for those who do not understand it.
|
83
84
|
|
84
|
-
###
|
85
|
+
### What's the deal with the tokenizers?
|
86
|
+
|
87
|
+
The Toki Pona tokenizer `word_tokenize_tok` is very specific in always separating writing characters from punctuation, and leaving contiguous punctuation as contiguous- this is a level of precision that NLTK's English tokenizer does not want for several reasons, such as that English words can have "punctuation" characters in them.
|
88
|
+
|
89
|
+
Toki Pona doesn't have any mid-word symbols when rendered in the Latin alphabet, so a more aggressive tokenizer is highly desirable.
|
90
|
+
|
91
|
+
The other tokenizers are provided as a comparison case more than anything. I do not recommend their use.
|
92
|
+
|
93
|
+
### Aren't there a lot of false positives?
|
94
|
+
|
95
|
+
Yes. It's up to you to use this tool responsibly on input you've done your best to clean, and better, use stronger filters before weaker ones. For now though, here's a list of relevant false positives:
|
96
|
+
|
97
|
+
- `ProperName` will errantly match text in languages without a capital/lowercase distinction, artificially inflating the scores.
|
98
|
+
- `Alphabetic` will match a _lot_ of undesirable text- it essentially allows 14 letters of the English alphabet.
|
99
|
+
|
100
|
+
### Don't some of the cleaners/filters conflict?
|
101
|
+
|
102
|
+
Yes. Some do so
|
103
|
+
|
104
|
+
- `ConsecutiveDuplicates` may errantly change a word's validity. For example, "manna" is phonotactically invalid in Toki Pona, but would become "mana" which is valid.
|
105
|
+
- `ConsecutiveDuplicates` will not work correctly with syllabaries (alphabets, but representing a pair of consonant and vowel).
|
106
|
+
|
107
|
+
You'll notice a _lot_ of these are troubles regarding the application of latin alphabet filters to non-latin text. Working on it!
|
@@ -30,7 +30,7 @@ from sonatoki.Filters import (
|
|
30
30
|
ProperName,
|
31
31
|
Punctuations,
|
32
32
|
)
|
33
|
-
from sonatoki.Scorers import
|
33
|
+
from sonatoki.Scorers import SoftScaling
|
34
34
|
from sonatoki.Cleaners import ConsecutiveDuplicates
|
35
35
|
from sonatoki.Tokenizers import word_tokenize_tok
|
36
36
|
from sonatoki.Preprocessors import URLs, DiscordEmotes
|
@@ -41,22 +41,23 @@ def main():
|
|
41
41
|
ignoring_filters=[Numerics, Punctuations],
|
42
42
|
scoring_filters=[NimiLinku, Syllabic, ProperName, Alphabetic],
|
43
43
|
cleaners=[ConsecutiveDuplicates],
|
44
|
-
scorer=
|
44
|
+
scorer=SoftScaling,
|
45
45
|
tokenizer=word_tokenize_tok,
|
46
46
|
)
|
47
47
|
ilo.is_toki_pona("imagine how is touch the sky") # False
|
48
48
|
ilo.is_toki_pona("o pilin insa e ni: sina pilin e sewi") # True
|
49
|
+
ilo.is_toki_pona("I Think I Can Evade Detection") # False
|
49
50
|
|
50
51
|
if __name__ == "__main__":
|
51
52
|
main()
|
52
53
|
```
|
53
54
|
|
54
|
-
`Ilo` is highly configurable by design, so I recommend exploring the `Preprocessors`, `Filters`, and `Scorers` modules. The `Cleaners` module only contains one cleaner, which I
|
55
|
+
`Ilo` is highly configurable by design, so I recommend exploring the `Preprocessors`, `Filters`, and `Scorers` modules. The `Cleaners` module only contains one cleaner, which I recommend using. The `Tokenizers` module contains several other word tokenizers, but their performance will be worse than the dedicated Toki Pona tokenizer `word_tokenize_tok`.
|
55
56
|
|
56
57
|
## Development
|
57
58
|
|
58
59
|
1. Install [pdm](https://github.com/pdm-project/pdm)
|
59
|
-
1. `pdm
|
60
|
+
1. `pdm install --dev`
|
60
61
|
1. Open any file you like!
|
61
62
|
|
62
63
|
## FAQ
|
@@ -67,4 +68,26 @@ The intent is to show our methodology to the Unicode Consortium, particularly to
|
|
67
68
|
|
68
69
|
After our proposal has been examined and a result given by the committee, I will translate this file and library into Toki Pona, with a note left behind for those who do not understand it.
|
69
70
|
|
70
|
-
###
|
71
|
+
### What's the deal with the tokenizers?
|
72
|
+
|
73
|
+
The Toki Pona tokenizer `word_tokenize_tok` is very specific in always separating writing characters from punctuation, and leaving contiguous punctuation as contiguous- this is a level of precision that NLTK's English tokenizer does not want for several reasons, such as that English words can have "punctuation" characters in them.
|
74
|
+
|
75
|
+
Toki Pona doesn't have any mid-word symbols when rendered in the Latin alphabet, so a more aggressive tokenizer is highly desirable.
|
76
|
+
|
77
|
+
The other tokenizers are provided as a comparison case more than anything. I do not recommend their use.
|
78
|
+
|
79
|
+
### Aren't there a lot of false positives?
|
80
|
+
|
81
|
+
Yes. It's up to you to use this tool responsibly on input you've done your best to clean, and better, use stronger filters before weaker ones. For now though, here's a list of relevant false positives:
|
82
|
+
|
83
|
+
- `ProperName` will errantly match text in languages without a capital/lowercase distinction, artificially inflating the scores.
|
84
|
+
- `Alphabetic` will match a _lot_ of undesirable text- it essentially allows 14 letters of the English alphabet.
|
85
|
+
|
86
|
+
### Don't some of the cleaners/filters conflict?
|
87
|
+
|
88
|
+
Yes. Some do so
|
89
|
+
|
90
|
+
- `ConsecutiveDuplicates` may errantly change a word's validity. For example, "manna" is phonotactically invalid in Toki Pona, but would become "mana" which is valid.
|
91
|
+
- `ConsecutiveDuplicates` will not work correctly with syllabaries (alphabets, but representing a pair of consonant and vowel).
|
92
|
+
|
93
|
+
You'll notice a _lot_ of these are troubles regarding the application of latin alphabet filters to non-latin text. Working on it!
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# STL
|
2
2
|
import math
|
3
|
+
import logging
|
3
4
|
from abc import ABC, abstractmethod
|
4
5
|
from typing import Dict, List, Type, Union
|
5
6
|
|
@@ -9,24 +10,13 @@ from typing_extensions import override
|
|
9
10
|
# LOCAL
|
10
11
|
from sonatoki.Filters import Filter
|
11
12
|
|
13
|
+
LOG = logging.getLogger(__name__)
|
14
|
+
|
12
15
|
Number = Union[int, float]
|
13
16
|
Weights = Dict[str, Number]
|
14
17
|
|
15
18
|
|
16
19
|
class Scorer(ABC):
|
17
|
-
weights: Weights
|
18
|
-
|
19
|
-
# @classmethod
|
20
|
-
# def __score(cls, token: str, filters: List[Type[Filter]]) -> Tuple[int, Number]:
|
21
|
-
# for filter in filters:
|
22
|
-
# if not filter.filter(token):
|
23
|
-
# continue
|
24
|
-
# # NOTE: We assume the filters are ordered by their score
|
25
|
-
# # Thus the first match is also the highest scoring
|
26
|
-
# return filter.counts, cls.weights[filter.__name__]
|
27
|
-
# # TODO: override weight if count is 0?
|
28
|
-
# return 1, 0
|
29
|
-
|
30
20
|
@classmethod
|
31
21
|
@abstractmethod
|
32
22
|
def score(cls, tokens: List[str], filters: List[Type[Filter]]) -> Number:
|
@@ -40,7 +30,12 @@ class PassFail(Scorer):
|
|
40
30
|
def __score(cls, token: str, filters: List[Type[Filter]]) -> Number:
|
41
31
|
for f in filters:
|
42
32
|
if f.filter(token):
|
43
|
-
|
33
|
+
score = 1
|
34
|
+
LOG.debug(
|
35
|
+
"%12s.%s('%s') = %.2f", cls.__name__, f.__name__, token, score
|
36
|
+
)
|
37
|
+
return score
|
38
|
+
LOG.debug("%12s('%s') = 0.00", cls.__name__, token)
|
44
39
|
return 0
|
45
40
|
|
46
41
|
@classmethod
|
@@ -67,7 +62,12 @@ class Scaling(Scorer):
|
|
67
62
|
def score_token(cls, token: str, filters: List[Type[Filter]], scale: int):
|
68
63
|
for i, f in enumerate(filters):
|
69
64
|
if f.filter(token):
|
70
|
-
|
65
|
+
score = scale - i
|
66
|
+
LOG.debug(
|
67
|
+
"%12s.%s('%s') = %.2f", cls.__name__, f.__name__, token, score
|
68
|
+
)
|
69
|
+
return score
|
70
|
+
LOG.debug("%12s('%s') = 0.00", cls.__name__, token)
|
71
71
|
return 0
|
72
72
|
|
73
73
|
@classmethod
|
@@ -95,7 +95,7 @@ class SoftScaling(Scaling):
|
|
95
95
|
def sigmoid(n: int) -> Number:
|
96
96
|
return 1 / (1 + math.exp(-(0.30 * (n - 1))))
|
97
97
|
# n-1 makes sigmoid(1) == 0.5
|
98
|
-
# 0.30 softens scaling
|
98
|
+
# 0.30 softens scaling in favor of short input
|
99
99
|
# return n / (1+abs(n)) # too weak in 0.7+
|
100
100
|
|
101
101
|
@classmethod
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# STL
|
2
|
-
|
2
|
+
import logging
|
3
|
+
from typing import List, Type, Tuple
|
3
4
|
|
4
5
|
# LOCAL
|
5
6
|
from sonatoki.Filters import Filter
|
@@ -8,6 +9,8 @@ from sonatoki.Cleaners import Cleaner
|
|
8
9
|
from sonatoki.Tokenizers import Tokenizer
|
9
10
|
from sonatoki.Preprocessors import Preprocessor
|
10
11
|
|
12
|
+
LOG = logging.getLogger(__name__)
|
13
|
+
|
11
14
|
|
12
15
|
class Ilo:
|
13
16
|
__preprocessors: List[Type[Preprocessor]]
|
@@ -17,7 +20,7 @@ class Ilo:
|
|
17
20
|
__scorer: Type[Scorer]
|
18
21
|
__tokenize: Tokenizer
|
19
22
|
__passing_score: Number
|
20
|
-
|
23
|
+
logging_threshold: Number = 1.0
|
21
24
|
|
22
25
|
def __init__(
|
23
26
|
self,
|
@@ -83,19 +86,35 @@ class Ilo:
|
|
83
86
|
def __score_tokens(self, tokens: List[str]) -> float:
|
84
87
|
return self.__scorer.score(tokens, self.__scoring_filters)
|
85
88
|
|
86
|
-
def
|
89
|
+
def _is_toki_pona(
|
90
|
+
self, message: str
|
91
|
+
) -> Tuple[str, List[str], List[str], List[str], Number, bool]:
|
92
|
+
"""Returns all components of the processing algorithm:
|
93
|
+
- Preprocessed message (str)
|
94
|
+
- Tokenized message (list[str])
|
95
|
+
- Filtered message (list[str])
|
96
|
+
- Cleaned message (list[str])
|
97
|
+
- Score (float)
|
98
|
+
- Result (bool)
|
99
|
+
"""
|
87
100
|
preprocessed = self.__preprocess(message)
|
88
101
|
tokenized = self.__tokenize(preprocessed)
|
89
102
|
filtered = self.__filter_tokens(tokenized)
|
90
103
|
cleaned = self.__clean_tokens(filtered)
|
91
104
|
score = self.__score_tokens(cleaned)
|
105
|
+
result = score >= self.__passing_score
|
92
106
|
|
93
|
-
if
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
107
|
+
# NOTE: this method may break if above funcs start sharing a list
|
108
|
+
if score <= self.logging_threshold:
|
109
|
+
LOG.debug("Msg: %.2f %s", score, repr(message))
|
110
|
+
LOG.debug("Preproc: %s", repr(preprocessed))
|
111
|
+
LOG.debug("Tokenized: %s", tokenized)
|
112
|
+
LOG.debug("Filtered: %s", filtered)
|
113
|
+
LOG.debug("Cleaned: %s", cleaned)
|
114
|
+
# TODO: Move to each function? Loses ability to control when logging occurs by threshold
|
100
115
|
|
101
|
-
return score
|
116
|
+
return preprocessed, tokenized, filtered, cleaned, score, result
|
117
|
+
|
118
|
+
def is_toki_pona(self, message: str) -> bool:
|
119
|
+
*_, result = self._is_toki_pona(message)
|
120
|
+
return result
|
@@ -36,18 +36,38 @@ def test_constructor():
|
|
36
36
|
tokenizer=word_tokenize_tok,
|
37
37
|
passing_score=0.8,
|
38
38
|
)
|
39
|
-
ilo.
|
40
|
-
assert not ilo.is_toki_pona("super bruh moment 64")
|
39
|
+
# ilo._logging_threshold = 0.8
|
41
40
|
assert ilo.is_toki_pona("mi unpa e mama sina")
|
42
|
-
|
43
|
-
assert ilo.is_toki_pona("
|
41
|
+
# toki pona
|
42
|
+
assert ilo.is_toki_pona("mama sina li lon seme? mi wile toki tawa ona")
|
43
|
+
assert ilo.is_toki_pona("sina sike pakala")
|
44
|
+
# names
|
44
45
|
assert ilo.is_toki_pona("musi Homestuck li ike tawa mi")
|
46
|
+
# typoes
|
45
47
|
assert ilo.is_toki_pona("mi mtue o kama sona")
|
48
|
+
assert ilo.is_toki_pona("mi mute o kma son")
|
49
|
+
# phonotactically valid
|
46
50
|
assert ilo.is_toki_pona("ni li tenpo penpo")
|
51
|
+
# alphabetically valid
|
47
52
|
assert ilo.is_toki_pona("ni li tptpt")
|
53
|
+
# a single
|
54
|
+
assert ilo.is_toki_pona("sipisi")
|
55
|
+
|
56
|
+
# soft scaling with syllablic filter at 2/4 will pass up to 5 syllablic words
|
57
|
+
assert ilo.is_toki_pona("walawa malama walama malama mupi")
|
58
|
+
# but fail 6 or more
|
59
|
+
assert not ilo.is_toki_pona("manama manama namana namana majani makala")
|
48
60
|
|
49
|
-
|
61
|
+
# TODO: should soft scaling save an alphabetically valid single word?
|
62
|
+
assert not ilo.is_toki_pona("tok")
|
63
|
+
assert not ilo.is_toki_pona("mtue")
|
64
|
+
|
65
|
+
# just english
|
66
|
+
assert not ilo.is_toki_pona("bong")
|
67
|
+
assert not ilo.is_toki_pona("super bruh moment 64")
|
68
|
+
# all names
|
69
|
+
assert not ilo.is_toki_pona("I Want To Evade The Filter")
|
70
|
+
# all alphabetic
|
50
71
|
assert not ilo.is_toki_pona(
|
51
|
-
"
|
52
|
-
it's as asinine as in `e`-less speak"""
|
72
|
+
"aaa i non-saw usa's most multiple element-set. it's as asinine as in `e`-less speak"
|
53
73
|
)
|
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
|
File without changes
|
File without changes
|