sloprefine 1.6.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.
Files changed (56) hide show
  1. sloprefine-1.6.1/.github/workflows/ci.yml +35 -0
  2. sloprefine-1.6.1/.github/workflows/publish.yml +47 -0
  3. sloprefine-1.6.1/.gitignore +8 -0
  4. sloprefine-1.6.1/.sloprefine.toml +20 -0
  5. sloprefine-1.6.1/CITATION.cff +66 -0
  6. sloprefine-1.6.1/CONTRIBUTING.md +72 -0
  7. sloprefine-1.6.1/LICENSE +21 -0
  8. sloprefine-1.6.1/PKG-INFO +554 -0
  9. sloprefine-1.6.1/README.md +526 -0
  10. sloprefine-1.6.1/assets/cadence.svg +54 -0
  11. sloprefine-1.6.1/assets/refinery.svg +95 -0
  12. sloprefine-1.6.1/assets/wanted.svg +58 -0
  13. sloprefine-1.6.1/calibration/README.md +65 -0
  14. sloprefine-1.6.1/calibration/fiction-2023.json +73 -0
  15. sloprefine-1.6.1/corpus/audit-demo/README.md +16 -0
  16. sloprefine-1.6.1/corpus/audit-demo/human/0.txt +1 -0
  17. sloprefine-1.6.1/corpus/audit-demo/human/1.txt +1 -0
  18. sloprefine-1.6.1/corpus/audit-demo/human/2.txt +1 -0
  19. sloprefine-1.6.1/corpus/audit-demo/human/3.txt +1 -0
  20. sloprefine-1.6.1/corpus/audit-demo/human/4.txt +1 -0
  21. sloprefine-1.6.1/corpus/audit-demo/human/5.txt +1 -0
  22. sloprefine-1.6.1/corpus/audit-demo/machine/0.txt +1 -0
  23. sloprefine-1.6.1/corpus/audit-demo/machine/1.txt +1 -0
  24. sloprefine-1.6.1/corpus/audit-demo/machine/2.txt +1 -0
  25. sloprefine-1.6.1/corpus/audit-demo/machine/3.txt +1 -0
  26. sloprefine-1.6.1/corpus/audit-demo/machine/4.txt +1 -0
  27. sloprefine-1.6.1/corpus/audit-demo/machine/5.txt +1 -0
  28. sloprefine-1.6.1/corpus/choppy_control.txt +9 -0
  29. sloprefine-1.6.1/corpus/clean_control.txt +7 -0
  30. sloprefine-1.6.1/corpus/slop_control.txt +5 -0
  31. sloprefine-1.6.1/pyproject.toml +45 -0
  32. sloprefine-1.6.1/src/sloprefine/__init__.py +50 -0
  33. sloprefine-1.6.1/src/sloprefine/__main__.py +3 -0
  34. sloprefine-1.6.1/src/sloprefine/agent.py +329 -0
  35. sloprefine-1.6.1/src/sloprefine/align.py +141 -0
  36. sloprefine-1.6.1/src/sloprefine/audit.py +206 -0
  37. sloprefine-1.6.1/src/sloprefine/cadence.py +214 -0
  38. sloprefine-1.6.1/src/sloprefine/calibration.py +157 -0
  39. sloprefine-1.6.1/src/sloprefine/checks.py +362 -0
  40. sloprefine-1.6.1/src/sloprefine/cli.py +433 -0
  41. sloprefine-1.6.1/src/sloprefine/mcp_server.py +214 -0
  42. sloprefine-1.6.1/src/sloprefine/metrics.py +83 -0
  43. sloprefine-1.6.1/src/sloprefine/paragraph.py +137 -0
  44. sloprefine-1.6.1/src/sloprefine/parallel.py +363 -0
  45. sloprefine-1.6.1/src/sloprefine/person.py +110 -0
  46. sloprefine-1.6.1/src/sloprefine/punch.py +219 -0
  47. sloprefine-1.6.1/src/sloprefine/reader.py +342 -0
  48. sloprefine-1.6.1/src/sloprefine/report.py +318 -0
  49. sloprefine-1.6.1/src/sloprefine/rules.py +350 -0
  50. sloprefine-1.6.1/src/sloprefine/scorer.py +137 -0
  51. sloprefine-1.6.1/src/sloprefine/stylometry.py +172 -0
  52. sloprefine-1.6.1/src/sloprefine/templates.py +255 -0
  53. sloprefine-1.6.1/src/sloprefine/text.py +172 -0
  54. sloprefine-1.6.1/src/sloprefine/voice.py +195 -0
  55. sloprefine-1.6.1/src/sloprefine/weighting.py +99 -0
  56. sloprefine-1.6.1/tests/test_sloprefine.py +1757 -0
@@ -0,0 +1,35 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ # mcp too, or the nine MCP server tests skip and CI reports green
20
+ # without ever having exercised the agent-facing surface.
21
+ - run: pip install -e ".[dev,mcp]"
22
+ - run: pytest -q
23
+ - run: ruff check .
24
+ - name: dogfood (the docs must pass their own linter)
25
+ run: sloprefine README.md CONTRIBUTING.md --no-color
26
+ - name: agent surfaces smoke test
27
+ run: |
28
+ sloprefine rules
29
+ sloprefine prompt > /dev/null
30
+ sloprefine prompt --profile talk > /dev/null
31
+ sloprefine check corpus/slop_control.txt --format agent || true
32
+ sloprefine drift corpus/slop_control.txt corpus/clean_control.txt || true
33
+ sloprefine prompt --audience expert > /dev/null
34
+ sloprefine check corpus/choppy_control.txt --max-choppiness 0.17 && exit 1 || true
35
+ sloprefine audit --ai corpus/audit-demo/machine --human corpus/audit-demo/human
@@ -0,0 +1,47 @@
1
+ name: publish
2
+
3
+ # Trusted publishing: PyPI verifies this workflow's OIDC identity, so there is
4
+ # no API token stored in the repository and none to rotate or leak.
5
+ # Configure the publisher once at
6
+ # https://pypi.org/manage/account/publishing/ before the first release.
7
+
8
+ on:
9
+ release:
10
+ types: [published]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ publish:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ # Job-level permissions REPLACE the workflow-level block rather than
20
+ # adding to it, so contents:read has to be restated here or checkout
21
+ # runs with contents:none and cannot fetch the repository.
22
+ contents: read
23
+ id-token: write # required for trusted publishing
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ # Publish nothing that does not pass its own suite and its own linter.
31
+ - run: pip install -e ".[dev,mcp]"
32
+ - run: pytest -q
33
+ - run: ruff check .
34
+ - run: sloprefine README.md CONTRIBUTING.md --no-color
35
+
36
+ # The tag must match the version the package actually declares, or the
37
+ # release and the artifact disagree for the rest of time.
38
+ - name: tag matches the declared version
39
+ run: |
40
+ tag="${GITHUB_REF_NAME#v}"
41
+ declared=$(python -c "import sloprefine; print(sloprefine.__version__)")
42
+ test "$tag" = "$declared" || {
43
+ echo "tag $tag != declared version $declared"; exit 1; }
44
+
45
+ - run: pip install build
46
+ - run: python -m build
47
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .venv/
@@ -0,0 +1,20 @@
1
+ # sloprefine's own configuration, which doubles as a worked example.
2
+ # "landscape" and "robust" are on the excess-vocabulary list and are also
3
+ # ordinary words in this repository's subject matter.
4
+ [sloprefine]
5
+ allow = ["landscape", "robust"]
6
+
7
+ # The sentence floor. Off by default in code because it is the one rule with
8
+ # no citation behind it and it refuses a device human writers legitimately
9
+ # use. Enabled here as this repository's own house policy.
10
+ # runt_mode = "verbless" refuses "One plot." but keeps "It worked."
11
+ # runt_mode = "all" refuses everything under the floor
12
+ # Parallelism runs allowed per 1000 words. Documentation enumerates things
13
+ # for a living, so this repo's own prose runs higher than a talk would.
14
+ # A conference talk should sit at 1.0 or below.
15
+ parallel_budget_per_1k = 3.0
16
+
17
+ min_sentence_words = 5
18
+ runt_mode = "verbless"
19
+ allow_runts = ["Thank you."]
20
+ max_hits = 0
@@ -0,0 +1,66 @@
1
+ cff-version: 1.2.0
2
+ title: "sloprefine: a slop refinery for prose"
3
+ message: "If you use this software, please cite it as below."
4
+ type: software
5
+ authors:
6
+ - family-names: Rumenovski
7
+ given-names: Filip
8
+ affiliation: "Wayne State University"
9
+ email: "frumenovski@gmail.com"
10
+ repository-code: "https://github.com/filiprumenovski/sloprefine"
11
+ license: MIT
12
+ version: 1.6.1
13
+ date-released: 2026-09-22
14
+ abstract: >-
15
+ A dependency-free prose linter that flags the documented surface markers of
16
+ machine-generated text, measures cadence and syntactic template reuse, and
17
+ calibrates its own rule weights against a labelled corpus. Rules carry
18
+ citations to the studies that motivate them; rules that fail to discriminate
19
+ on a supplied corpus are reported rather than silently applied.
20
+ keywords:
21
+ - writing
22
+ - linter
23
+ - large language models
24
+ - stylometry
25
+ - text diversity
26
+ references:
27
+ - type: article
28
+ title: "Delving into LLM-assisted writing in biomedical publications through excess vocabulary"
29
+ authors:
30
+ - family-names: Kobak
31
+ given-names: Dmitry
32
+ year: 2025
33
+ journal: "Science Advances"
34
+ - type: conference-paper
35
+ title: "Detection and Measurement of Syntactic Templates in Generated Text"
36
+ authors:
37
+ - family-names: Shaib
38
+ given-names: Chantal
39
+ - family-names: Elazar
40
+ given-names: Yanai
41
+ - family-names: Li
42
+ given-names: "Junyi Jessy"
43
+ - family-names: Wallace
44
+ given-names: "Byron C."
45
+ year: 2024
46
+ collection-title: "EMNLP"
47
+ - type: article
48
+ title: "AI Writers Have a Consistent Stylometric Footprint, but AI Editors Do Not"
49
+ authors:
50
+ - family-names: Shan
51
+ given-names: Zhengyang
52
+ - family-names: Lee
53
+ given-names: Yukyung
54
+ - family-names: Hao
55
+ given-names: Sophie
56
+ year: 2026
57
+ journal: "arXiv:2608.27855"
58
+ - type: article
59
+ title: "GPT detectors are biased against non-native English writers"
60
+ authors:
61
+ - family-names: Liang
62
+ given-names: Weixin
63
+ - family-names: Zou
64
+ given-names: James
65
+ year: 2023
66
+ journal: "Patterns"
@@ -0,0 +1,72 @@
1
+ # Contributing
2
+
3
+ ## Adding a rule
4
+
5
+ Every rule needs a `fix`: one imperative sentence a model can act on without
6
+ reading the rationale. `why` explains the rule to a person; `fix` is what gets
7
+ shipped into the revision loop and into `sloprefine prompt`. A test asserts
8
+ every rule has one.
9
+
10
+ Every rule also needs a source, meaning not a vibe but something someone put
11
+ their name on: a study, published corpus statistics, or an argument someone
12
+ wrote down and signed. Add the short key and full
13
+ reference to the docstring at the top of `src/sloprefine/rules.py`, then declare
14
+ the rule with `_rule(...)` and give it a `why` that a writer can act on.
15
+
16
+ Two more things are required.
17
+
18
+ 1. A positive test in `tests/test_sloprefine.py::test_rule_fires`.
19
+ 2. No new hits on `corpus/clean_control.txt`. That file is a regression fence.
20
+ `test_clean_control_is_silent` asserts zero. If your rule fires there,
21
+ either the rule is too broad or the fence was wrong; argue for one.
22
+
23
+ ## False positives are the whole game
24
+
25
+ A prose linter that cries wolf gets uninstalled in a week, and then it protects
26
+ nobody. Prefer a rule that misses half the instances over one that fires on
27
+ ordinary writing. If a word is load-bearing in some field, add it to
28
+ `COMMONLY_LEGITIMATE` rather than dropping it from the lexicon.
29
+
30
+ ## Adding a reader-preference signal
31
+
32
+ The highest bar in the repository. A positive signal needs a direction
33
+ measured after 2022 by comparing human and machine text against real reader
34
+ judgments. "Good writers do this" is not admissible, and neither is a
35
+ pre-2022 prescription, because prescription is what put the pattern in the
36
+ training data in the first place. That is how the rule of three became a
37
+ detection marker.
38
+
39
+ Cite the corpus, say who the readers were, and give the direction of the
40
+ effect. Say which
41
+ audience it applies to; `reader.py` takes one because the evidence says the
42
+ two reader clusters want different things. If you approximate a feature the
43
+ source measured with a model, name the proxy in the code and in the output.
44
+
45
+ Then run `sloprefine audit` against a real corpus pair and report the
46
+ enrichment. A signal that does not separate machine text from human text on
47
+ current models is not a signal yet.
48
+
49
+ ## Adding a stylometric feature
50
+
51
+ The bar here is different. Features go in `stylometry.py`. They must be
52
+ computable offline, with no language model and nothing that needs
53
+ corpus-level statistics, so that a draft can be checked on a laptop and
54
+ the number still means the same thing tomorrow.
55
+
56
+ Length-correct anything derived from token counts. Type-token ratio and
57
+ Shannon entropy both fall with document length for arithmetic reasons, so an
58
+ uncorrected feature measures how long the document is and reports it as style.
59
+
60
+ Do not add a threshold. `stylometry.py` deliberately ships without population cutoffs. The reason
61
+ is in `voice.py`, where a cutoff transferred between corpora turns out to be
62
+ the documented failure mode of deployed detectors, landing hardest on
63
+ non-native speakers. If a feature is only meaningful against a threshold, it belongs in
64
+ the voiceprint layer as a deviation, not in the report as a judgement.
65
+
66
+ ## Running everything
67
+
68
+ ```bash
69
+ pip install -e ".[dev]"
70
+ pytest -q
71
+ ruff check .
72
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Filip Rumenovski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.