humanus 0.35.0__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 (70) hide show
  1. humanus-0.35.0/LICENSE +21 -0
  2. humanus-0.35.0/PKG-INFO +231 -0
  3. humanus-0.35.0/README.md +210 -0
  4. humanus-0.35.0/pyproject.toml +57 -0
  5. humanus-0.35.0/setup.cfg +4 -0
  6. humanus-0.35.0/src/humanus/__init__.py +51 -0
  7. humanus-0.35.0/src/humanus/__main__.py +8 -0
  8. humanus-0.35.0/src/humanus/_util.py +18 -0
  9. humanus-0.35.0/src/humanus/builtin_checks.py +312 -0
  10. humanus-0.35.0/src/humanus/calibration.py +131 -0
  11. humanus-0.35.0/src/humanus/check.py +238 -0
  12. humanus-0.35.0/src/humanus/classifier_lane.py +390 -0
  13. humanus-0.35.0/src/humanus/cli.py +80 -0
  14. humanus-0.35.0/src/humanus/contract_commands.py +128 -0
  15. humanus-0.35.0/src/humanus/contracts.py +84 -0
  16. humanus-0.35.0/src/humanus/data/SKILL.md +58 -0
  17. humanus-0.35.0/src/humanus/data/__init__.py +1 -0
  18. humanus-0.35.0/src/humanus/emoji.py +11 -0
  19. humanus-0.35.0/src/humanus/finding_ids.py +38 -0
  20. humanus-0.35.0/src/humanus/language_checks.py +290 -0
  21. humanus-0.35.0/src/humanus/languages/__init__.py +202 -0
  22. humanus-0.35.0/src/humanus/languages/en.yml +74 -0
  23. humanus-0.35.0/src/humanus/languages/es-419.yml +14 -0
  24. humanus-0.35.0/src/humanus/languages/es.yml +49 -0
  25. humanus-0.35.0/src/humanus/languages/zh-Hans.yml +90 -0
  26. humanus-0.35.0/src/humanus/openai_responses.py +94 -0
  27. humanus-0.35.0/src/humanus/packs/NOTICE.md +67 -0
  28. humanus-0.35.0/src/humanus/packs/__init__.py +27 -0
  29. humanus-0.35.0/src/humanus/packs/ai-writing-tells-es.yml +56 -0
  30. humanus-0.35.0/src/humanus/packs/ai-writing-tells-zh-hans.yml +105 -0
  31. humanus-0.35.0/src/humanus/packs/ai-writing-tells.yml +1123 -0
  32. humanus-0.35.0/src/humanus/packs/asd-ste100.yml +179 -0
  33. humanus-0.35.0/src/humanus/packs/plain-language.yml +322 -0
  34. humanus-0.35.0/src/humanus/packs/translationese-zh-hans.yml +72 -0
  35. humanus-0.35.0/src/humanus/packs/typography-es.yml +31 -0
  36. humanus-0.35.0/src/humanus/packs/typography-zh-hans.yml +68 -0
  37. humanus-0.35.0/src/humanus/packs/word-choice-es-419.yml +32 -0
  38. humanus-0.35.0/src/humanus/policy_pack.py +283 -0
  39. humanus-0.35.0/src/humanus/rules_lane.py +132 -0
  40. humanus-0.35.0/src/humanus/schemas/calibration-v1.json +46 -0
  41. humanus-0.35.0/src/humanus/schemas/check-request-v1.json +137 -0
  42. humanus-0.35.0/src/humanus/schemas/check-result-v1.json +316 -0
  43. humanus-0.35.0/src/humanus/schemas/draft-questions-request-v1.json +57 -0
  44. humanus-0.35.0/src/humanus/schemas/draft-questions-result-v1.json +33 -0
  45. humanus-0.35.0/src/humanus/schemas/feedback-labels-v1.json +47 -0
  46. humanus-0.35.0/src/humanus/schemas/finding-v1.json +105 -0
  47. humanus-0.35.0/src/humanus/schemas/policy-pack-v1.json +535 -0
  48. humanus-0.35.0/src/humanus/schemas/suggest-request-v1.json +79 -0
  49. humanus-0.35.0/src/humanus/schemas/suggest-result-v1.json +104 -0
  50. humanus-0.35.0/src/humanus/segmentation.py +440 -0
  51. humanus-0.35.0/src/humanus/skill.py +13 -0
  52. humanus-0.35.0/src/humanus/standard_checks.py +270 -0
  53. humanus-0.35.0/src/humanus/style_profile_import.py +181 -0
  54. humanus-0.35.0/src/humanus/suggest.py +423 -0
  55. humanus-0.35.0/src/humanus/usability_commands.py +38 -0
  56. humanus-0.35.0/src/humanus/usability_profile.py +256 -0
  57. humanus-0.35.0/src/humanus/usability_scan.py +1024 -0
  58. humanus-0.35.0/src/humanus.egg-info/PKG-INFO +231 -0
  59. humanus-0.35.0/src/humanus.egg-info/SOURCES.txt +68 -0
  60. humanus-0.35.0/src/humanus.egg-info/dependency_links.txt +1 -0
  61. humanus-0.35.0/src/humanus.egg-info/entry_points.txt +2 -0
  62. humanus-0.35.0/src/humanus.egg-info/requires.txt +10 -0
  63. humanus-0.35.0/src/humanus.egg-info/top_level.txt +1 -0
  64. humanus-0.35.0/tests/test_builtin_checks.py +69 -0
  65. humanus-0.35.0/tests/test_policy_pack_contracts.py +148 -0
  66. humanus-0.35.0/tests/test_readme_examples.py +35 -0
  67. humanus-0.35.0/tests/test_skill_cli.py +45 -0
  68. humanus-0.35.0/tests/test_standard_packs.py +106 -0
  69. humanus-0.35.0/tests/test_tables_and_quotations.py +75 -0
  70. humanus-0.35.0/tests/test_usability_scan.py +482 -0
humanus-0.35.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anthus AI Solutions
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.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: humanus
3
+ Version: 0.35.0
4
+ Summary: Configurable editorial compliance checks: policy packs, deterministic rules, zero-shot decision models and rewrite suggestions.
5
+ Author: Anthus AI Solutions
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/AnthusAI/Humanus
8
+ Project-URL: Repository, https://github.com/AnthusAI/Humanus
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: PyYAML<7.0.0,>=6.0.2
13
+ Requires-Dist: jsonschema<5,>=4.18
14
+ Requires-Dist: markdown-it-py<5,>=3.0
15
+ Provides-Extra: jev
16
+ Requires-Dist: typesafe-sdk<0.8,>=0.7.2; extra == "jev"
17
+ Provides-Extra: dev
18
+ Requires-Dist: behave>=1.2.6; extra == "dev"
19
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # Humanus
23
+
24
+ Humanus is a configurable editorial compliance engine. A publication sends its
25
+ own editorial policies with each request; Humanus marks every place a draft
26
+ breaks them, says how sure it is, and offers rewrites when an editor asks.
27
+
28
+ ## Press Release
29
+
30
+ **Anthus AI Solutions introduces Humanus, an open-source engine that checks writing against a publication's own editorial policies — and only rewrites when an editor asks**
31
+
32
+ Every publication has rules: words it avoids, claims it will not make without a
33
+ source, a voice it keeps. Most are enforced by an editor's memory. Humanus makes
34
+ them executable. A CMS sends a draft and a *policy pack* — the publication's
35
+ policies, each written in plain words and backed by detectors — and Humanus
36
+ returns the draft annotated with every finding, down to the exact words.
37
+
38
+ Two kinds of detector do the finding. Rules catch what a pattern can: banned
39
+ phrases, house style, the tells of AI-generated prose. A decision model, TypeSafe
40
+ Jev, judges what a pattern cannot — "does this sentence make a claim without
41
+ saying who or how much?" — sentence by sentence, with a probability attached.
42
+ Rewrites come from a generative model only when an editor asks for them, for a
43
+ flagged passage or any passage they select, and they never add facts.
44
+
45
+ "We wanted editorial policy to be something you can read, test and change, not
46
+ something one editor carries in their head," said the Humanus team at Anthus AI
47
+ Solutions. Humanus runs inside Papyrus, Anthus's newsroom system, and is
48
+ available as an MIT-licensed Python package.
49
+
50
+ ## FAQ
51
+
52
+ **Is Humanus an AI-content detector?** No. It checks text against policies you
53
+ define, whoever wrote it. The starter pack of AI-writing tells is a set of
54
+ editable policies like any other.
55
+
56
+ **Can it enforce a published standard?** Yes. Starter packs implement ASD-STE100
57
+ Simplified Technical English (`asd-ste100`) and the Federal Plain Language
58
+ Guidelines (`plain-language`); copy one and adjust its limits and word lists.
59
+
60
+ **Does it rewrite my content?** Only when asked, and never by itself: `suggest`
61
+ returns options for a finding or a selection, and a person chooses.
62
+
63
+ **Where do policies live?** In the caller. Humanus is stateless; the CMS stores
64
+ policies, settings and editors' decisions and sends a pack with every request.
65
+
66
+ ## Installation
67
+
68
+ ```bash
69
+ pip install humanus # rules lane, contracts, suggest
70
+ pip install "humanus[jev]" # adds the TypeSafe Jev classifier lane
71
+ ```
72
+
73
+ Set `TYPESAFE_API_KEY` for the classifier lane and `OPENAI_API_KEY` for
74
+ rewrite suggestions and drafted questions (`gpt-6-luna` by default).
75
+
76
+ ## Usage
77
+
78
+ ```bash
79
+ humanus pack builtin ai-writing-tells > pack.json # a starter pack to edit
80
+ humanus pack import --profile style-profile.yml > pack.json # or convert a legacy style profile
81
+ humanus check --draft draft.md --pack pack.json --language en # findings, segments, lanes (check-result-v1)
82
+ humanus suggest --request suggest-request.json # rewrite options for a finding or selection
83
+ humanus draft-questions --request draft-request.json # classifier detectors for a prose policy
84
+ humanus calibrate --labels labels.json # calibration from editors' decisions
85
+ humanus schema policy-pack-v1 # print any contract schema
86
+ ```
87
+
88
+ ```python
89
+ from humanus import check, suggest
90
+
91
+ result = check({"schemaVersion": 1, "content": {"format": "markus", "language": "en", "text": text}, "pack": pack})
92
+ for finding in result["findings"]:
93
+ print(finding["severity"], finding["policyId"], finding["excerpt"], finding.get("probability"))
94
+ ```
95
+
96
+ ## Contracts
97
+
98
+ The payloads are JSON Schemas packaged with Humanus (`humanus schema <name>`);
99
+ callers in any language validate against the same files.
100
+
101
+ | Schema | Purpose |
102
+ | --- | --- |
103
+ | `policy-pack-v1` | Policies: prose guidance, severity (`info`/`warn`/`error`), examples, rewrite guidance and detectors — `rules` (`regex`, `phrase`, `builtin`) or `classifier` (a zero-shot question at sentence, paragraph or document scope, with threshold, review band and optional calibration) |
104
+ | `check-request-v1` / `check-result-v1` | Content (`markus`, `markdown` or `text`, with its `language`) plus a pack in; the language pack used, segments, protected regions, findings, coverage and lane status out |
105
+ | `finding-v1` | One located problem, with an id that is stable across rescans |
106
+ | `suggest-request-v1` / `suggest-result-v1` | Rewrite options for a finding (rewritten as its sentence) or an editor's selection |
107
+ | `draft-questions-request-v1` / `draft-questions-result-v1` | Classifier detectors drafted from a prose policy |
108
+ | `feedback-labels-v1` / `calibration-v1` | Editors' decisions in, per-detector calibration out |
109
+
110
+ **Offsets** count UTF-16 code units, as JavaScript string indexes do.
111
+ **Segments** are the document, its prose paragraphs (paragraphs, headings, list
112
+ items) and their sentences; each carries its plain text so a rendered preview
113
+ can place findings. Front matter, code, Markus directives, block quotes and raw
114
+ HTML are protected regions and never checked. Table cells are prose: each cell
115
+ that has words in it is a paragraph segment with block `tableCell`, split into
116
+ sentences like any other paragraph, so both lanes read it and findings point at
117
+ the cell text; pipes and the alignment row stay out of detection, and cells that
118
+ hold only numbers or code are skipped. Set `options.tables` to `false` to make
119
+ tables a protected region again. Whole-document measures (average sentence
120
+ length, reading grade, information density, paragraph length, uniform cadence,
121
+ overused words) leave table cells out. Paragraph, sentence and
122
+ finding ids derive from text, so they survive edits elsewhere.
123
+
124
+ ## Languages
125
+
126
+ Every check request states the language of its content (`content.language`, a
127
+ BCP 47 tag) and every policy pack states the language its policies are written
128
+ for (`language`, or `"*"` for language-neutral policies). Humanus reads the text
129
+ with that language's **language pack** (`src/humanus/languages/*.yml`): where
130
+ sentences end (Chinese `。!?` with no space or capital after them; Spanish
131
+ `¿…?` and `¡…!`), what a unit of length is (words, or characters in Chinese),
132
+ how a phrase matches (on word boundaries, or anywhere in a script written
133
+ without spaces), which quotation marks enclose someone else's words, which
134
+ built-in checks the language can run, its word classes, its native baseline
135
+ rates and its typography data. Shipped: `en`, `es`, `es-419` (an overlay on
136
+ `es`: only what differs, such as quotation marks and number forms) and
137
+ `zh-Hans` (also served for `zh-CN`, `zh-SG`, `zh-MY`).
138
+
139
+ - A language with no pack is **refused** with an error that names it; text is
140
+ never checked as if it were English. Tags match exactly (or a documented
141
+ alias): `es-AR` is refused, not read as `es`.
142
+ - A pack written for another language is refused: English policies never run
143
+ on Chinese text. A pack for a base language (`es`) applies to its variants
144
+ (`es-419`); a pack for one variant never applies to another.
145
+ - A rules detector the language cannot run (an English built-in on Chinese, a
146
+ word class needing a tagger the pack lacks) is listed in
147
+ `lanes.rules.notRun` with the reason.
148
+
149
+ Generic built-ins take everything from the language pack: `frame` (ordered
150
+ parts with a bounded gap in one sentence, such as `不是 … 而是`), `density` (a
151
+ word class per paragraph against the pack's native rate, reported when the
152
+ count would be improbable at that rate) and `typography` (a named mechanical
153
+ rule: `punctuation-width`, `quotation-marks`, `ellipsis`,
154
+ `space-beside-full-width`, `latin-spacing`, `number-words`, `number-format`).
155
+ Length checks count in the language's unit.
156
+
157
+ ## Lanes
158
+
159
+ - **Rules** (deterministic): phrase and regex detectors run on prose only;
160
+ built-in checks cover empty lead-ins, unsupported certainty, missing
161
+ attribution, passive voice, redundancy, uniform cadence, information density,
162
+ overused words, uncontracted forms, contrast caps and emoji. Checks for writing
163
+ standards cover sentence length (by procedure or description), average
164
+ sentence length, paragraph length, Flesch-Kincaid grade, double negatives,
165
+ undefined acronyms, one instruction per step, imperative steps, -ing forms,
166
+ missing articles and one meaning per word. Phrase detectors take `exceptions`,
167
+ such as technical names, inside which a match is not a finding.
168
+ Direct quotations are someone else's words: a rules finding that lies
169
+ entirely inside a double-quoted passage (straight or curly) or a block quote
170
+ is not reported. A detector or its policy sets `quotations: "check"` to opt
171
+ back in (the detector wins); the starter pack does so for placeholder leaks,
172
+ chatbot artifacts, chat citation markup and AI-tool tracking parameters.
173
+ `passive_voice` also ignores a passive clause inside a quotation when the
174
+ rest of the sentence is not passive. The classifier lane still reads quotations.
175
+ - **Starter packs** (`humanus pack builtin <name>`):
176
+ - `ai-writing-tells`: 24 policies adapted from
177
+ [avoid-ai-writing](https://github.com/conorbronsdon/avoid-ai-writing) (MIT).
178
+ - `asd-ste100`: ASD-STE100 Simplified Technical English. The official
179
+ dictionary is licensed by ASD, so the pack ships a small seed of
180
+ unapproved words, nouns and one-meaning words; extend them with yours.
181
+ - `plain-language`: the Federal Plain Language Guidelines (public domain),
182
+ with simple-word and excess-word replacements, "must" for "shall", hidden
183
+ verbs, length and readability limits, and Jev questions for addressing the
184
+ reader as "you", putting the main point first and avoiding jargon.
185
+ - `typography-zh-hans`, `typography-es`: mechanical punctuation, quotation
186
+ mark and number-form rules, each finding with its deterministic replacement.
187
+ - `ai-writing-tells-zh-hans`, `translationese-zh-hans`, `ai-writing-tells-es`:
188
+ small advisory starter sets (all `info`), each policy marked with its
189
+ `evidence` grade (`measured`, `normative` or `folk`).
190
+ - `word-choice-es-419`: Latin American word choice (computadora, falla).
191
+
192
+ See `humanus/packs/NOTICE.md` for sources and licenses.
193
+ - **Classifier** (TypeSafe Jev, zero-shot): one request per segment carries
194
+ every question that applies to it; sentences are judged with their paragraph
195
+ as context. Answers are cached by content, so a rescan only asks about changed
196
+ text. `options.classifier` sets the model, concurrency, an input-token budget,
197
+ a deadline (to stay inside an API timeout) and few-shot examples from past
198
+ decisions. Without a decision model the lane reports `unavailable` and rules
199
+ findings are still returned.
200
+
201
+ Editors' decisions improve the classifier: `calibrate` fits Platt scaling (from
202
+ 10 labels) or isotonic regression (from 50) per detector; store it on the
203
+ detector as `calibration` and thresholds apply to the calibrated probability.
204
+
205
+ ## HTML usability
206
+
207
+ A separate, read-only scan for rendered HTML pages (contrast, tap targets, focus
208
+ visibility, alt text, generic AI-page patterns):
209
+
210
+ ```bash
211
+ humanus usability scan --page page.html --profile usability-profile.yml
212
+ ```
213
+
214
+ ## For agents
215
+
216
+ `humanus skill` prints an Agent Skill describing the check → suggest → choose
217
+ workflow.
218
+
219
+ ## Testing
220
+
221
+ Specifications are Gherkin, run with [Behave](https://behave.readthedocs.io/);
222
+ unit tests use pytest. Neither needs network access or API keys.
223
+
224
+ ```bash
225
+ behave
226
+ python -m pytest tests -q
227
+ ```
228
+
229
+ ## License
230
+
231
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,210 @@
1
+ # Humanus
2
+
3
+ Humanus is a configurable editorial compliance engine. A publication sends its
4
+ own editorial policies with each request; Humanus marks every place a draft
5
+ breaks them, says how sure it is, and offers rewrites when an editor asks.
6
+
7
+ ## Press Release
8
+
9
+ **Anthus AI Solutions introduces Humanus, an open-source engine that checks writing against a publication's own editorial policies — and only rewrites when an editor asks**
10
+
11
+ Every publication has rules: words it avoids, claims it will not make without a
12
+ source, a voice it keeps. Most are enforced by an editor's memory. Humanus makes
13
+ them executable. A CMS sends a draft and a *policy pack* — the publication's
14
+ policies, each written in plain words and backed by detectors — and Humanus
15
+ returns the draft annotated with every finding, down to the exact words.
16
+
17
+ Two kinds of detector do the finding. Rules catch what a pattern can: banned
18
+ phrases, house style, the tells of AI-generated prose. A decision model, TypeSafe
19
+ Jev, judges what a pattern cannot — "does this sentence make a claim without
20
+ saying who or how much?" — sentence by sentence, with a probability attached.
21
+ Rewrites come from a generative model only when an editor asks for them, for a
22
+ flagged passage or any passage they select, and they never add facts.
23
+
24
+ "We wanted editorial policy to be something you can read, test and change, not
25
+ something one editor carries in their head," said the Humanus team at Anthus AI
26
+ Solutions. Humanus runs inside Papyrus, Anthus's newsroom system, and is
27
+ available as an MIT-licensed Python package.
28
+
29
+ ## FAQ
30
+
31
+ **Is Humanus an AI-content detector?** No. It checks text against policies you
32
+ define, whoever wrote it. The starter pack of AI-writing tells is a set of
33
+ editable policies like any other.
34
+
35
+ **Can it enforce a published standard?** Yes. Starter packs implement ASD-STE100
36
+ Simplified Technical English (`asd-ste100`) and the Federal Plain Language
37
+ Guidelines (`plain-language`); copy one and adjust its limits and word lists.
38
+
39
+ **Does it rewrite my content?** Only when asked, and never by itself: `suggest`
40
+ returns options for a finding or a selection, and a person chooses.
41
+
42
+ **Where do policies live?** In the caller. Humanus is stateless; the CMS stores
43
+ policies, settings and editors' decisions and sends a pack with every request.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install humanus # rules lane, contracts, suggest
49
+ pip install "humanus[jev]" # adds the TypeSafe Jev classifier lane
50
+ ```
51
+
52
+ Set `TYPESAFE_API_KEY` for the classifier lane and `OPENAI_API_KEY` for
53
+ rewrite suggestions and drafted questions (`gpt-6-luna` by default).
54
+
55
+ ## Usage
56
+
57
+ ```bash
58
+ humanus pack builtin ai-writing-tells > pack.json # a starter pack to edit
59
+ humanus pack import --profile style-profile.yml > pack.json # or convert a legacy style profile
60
+ humanus check --draft draft.md --pack pack.json --language en # findings, segments, lanes (check-result-v1)
61
+ humanus suggest --request suggest-request.json # rewrite options for a finding or selection
62
+ humanus draft-questions --request draft-request.json # classifier detectors for a prose policy
63
+ humanus calibrate --labels labels.json # calibration from editors' decisions
64
+ humanus schema policy-pack-v1 # print any contract schema
65
+ ```
66
+
67
+ ```python
68
+ from humanus import check, suggest
69
+
70
+ result = check({"schemaVersion": 1, "content": {"format": "markus", "language": "en", "text": text}, "pack": pack})
71
+ for finding in result["findings"]:
72
+ print(finding["severity"], finding["policyId"], finding["excerpt"], finding.get("probability"))
73
+ ```
74
+
75
+ ## Contracts
76
+
77
+ The payloads are JSON Schemas packaged with Humanus (`humanus schema <name>`);
78
+ callers in any language validate against the same files.
79
+
80
+ | Schema | Purpose |
81
+ | --- | --- |
82
+ | `policy-pack-v1` | Policies: prose guidance, severity (`info`/`warn`/`error`), examples, rewrite guidance and detectors — `rules` (`regex`, `phrase`, `builtin`) or `classifier` (a zero-shot question at sentence, paragraph or document scope, with threshold, review band and optional calibration) |
83
+ | `check-request-v1` / `check-result-v1` | Content (`markus`, `markdown` or `text`, with its `language`) plus a pack in; the language pack used, segments, protected regions, findings, coverage and lane status out |
84
+ | `finding-v1` | One located problem, with an id that is stable across rescans |
85
+ | `suggest-request-v1` / `suggest-result-v1` | Rewrite options for a finding (rewritten as its sentence) or an editor's selection |
86
+ | `draft-questions-request-v1` / `draft-questions-result-v1` | Classifier detectors drafted from a prose policy |
87
+ | `feedback-labels-v1` / `calibration-v1` | Editors' decisions in, per-detector calibration out |
88
+
89
+ **Offsets** count UTF-16 code units, as JavaScript string indexes do.
90
+ **Segments** are the document, its prose paragraphs (paragraphs, headings, list
91
+ items) and their sentences; each carries its plain text so a rendered preview
92
+ can place findings. Front matter, code, Markus directives, block quotes and raw
93
+ HTML are protected regions and never checked. Table cells are prose: each cell
94
+ that has words in it is a paragraph segment with block `tableCell`, split into
95
+ sentences like any other paragraph, so both lanes read it and findings point at
96
+ the cell text; pipes and the alignment row stay out of detection, and cells that
97
+ hold only numbers or code are skipped. Set `options.tables` to `false` to make
98
+ tables a protected region again. Whole-document measures (average sentence
99
+ length, reading grade, information density, paragraph length, uniform cadence,
100
+ overused words) leave table cells out. Paragraph, sentence and
101
+ finding ids derive from text, so they survive edits elsewhere.
102
+
103
+ ## Languages
104
+
105
+ Every check request states the language of its content (`content.language`, a
106
+ BCP 47 tag) and every policy pack states the language its policies are written
107
+ for (`language`, or `"*"` for language-neutral policies). Humanus reads the text
108
+ with that language's **language pack** (`src/humanus/languages/*.yml`): where
109
+ sentences end (Chinese `。!?` with no space or capital after them; Spanish
110
+ `¿…?` and `¡…!`), what a unit of length is (words, or characters in Chinese),
111
+ how a phrase matches (on word boundaries, or anywhere in a script written
112
+ without spaces), which quotation marks enclose someone else's words, which
113
+ built-in checks the language can run, its word classes, its native baseline
114
+ rates and its typography data. Shipped: `en`, `es`, `es-419` (an overlay on
115
+ `es`: only what differs, such as quotation marks and number forms) and
116
+ `zh-Hans` (also served for `zh-CN`, `zh-SG`, `zh-MY`).
117
+
118
+ - A language with no pack is **refused** with an error that names it; text is
119
+ never checked as if it were English. Tags match exactly (or a documented
120
+ alias): `es-AR` is refused, not read as `es`.
121
+ - A pack written for another language is refused: English policies never run
122
+ on Chinese text. A pack for a base language (`es`) applies to its variants
123
+ (`es-419`); a pack for one variant never applies to another.
124
+ - A rules detector the language cannot run (an English built-in on Chinese, a
125
+ word class needing a tagger the pack lacks) is listed in
126
+ `lanes.rules.notRun` with the reason.
127
+
128
+ Generic built-ins take everything from the language pack: `frame` (ordered
129
+ parts with a bounded gap in one sentence, such as `不是 … 而是`), `density` (a
130
+ word class per paragraph against the pack's native rate, reported when the
131
+ count would be improbable at that rate) and `typography` (a named mechanical
132
+ rule: `punctuation-width`, `quotation-marks`, `ellipsis`,
133
+ `space-beside-full-width`, `latin-spacing`, `number-words`, `number-format`).
134
+ Length checks count in the language's unit.
135
+
136
+ ## Lanes
137
+
138
+ - **Rules** (deterministic): phrase and regex detectors run on prose only;
139
+ built-in checks cover empty lead-ins, unsupported certainty, missing
140
+ attribution, passive voice, redundancy, uniform cadence, information density,
141
+ overused words, uncontracted forms, contrast caps and emoji. Checks for writing
142
+ standards cover sentence length (by procedure or description), average
143
+ sentence length, paragraph length, Flesch-Kincaid grade, double negatives,
144
+ undefined acronyms, one instruction per step, imperative steps, -ing forms,
145
+ missing articles and one meaning per word. Phrase detectors take `exceptions`,
146
+ such as technical names, inside which a match is not a finding.
147
+ Direct quotations are someone else's words: a rules finding that lies
148
+ entirely inside a double-quoted passage (straight or curly) or a block quote
149
+ is not reported. A detector or its policy sets `quotations: "check"` to opt
150
+ back in (the detector wins); the starter pack does so for placeholder leaks,
151
+ chatbot artifacts, chat citation markup and AI-tool tracking parameters.
152
+ `passive_voice` also ignores a passive clause inside a quotation when the
153
+ rest of the sentence is not passive. The classifier lane still reads quotations.
154
+ - **Starter packs** (`humanus pack builtin <name>`):
155
+ - `ai-writing-tells`: 24 policies adapted from
156
+ [avoid-ai-writing](https://github.com/conorbronsdon/avoid-ai-writing) (MIT).
157
+ - `asd-ste100`: ASD-STE100 Simplified Technical English. The official
158
+ dictionary is licensed by ASD, so the pack ships a small seed of
159
+ unapproved words, nouns and one-meaning words; extend them with yours.
160
+ - `plain-language`: the Federal Plain Language Guidelines (public domain),
161
+ with simple-word and excess-word replacements, "must" for "shall", hidden
162
+ verbs, length and readability limits, and Jev questions for addressing the
163
+ reader as "you", putting the main point first and avoiding jargon.
164
+ - `typography-zh-hans`, `typography-es`: mechanical punctuation, quotation
165
+ mark and number-form rules, each finding with its deterministic replacement.
166
+ - `ai-writing-tells-zh-hans`, `translationese-zh-hans`, `ai-writing-tells-es`:
167
+ small advisory starter sets (all `info`), each policy marked with its
168
+ `evidence` grade (`measured`, `normative` or `folk`).
169
+ - `word-choice-es-419`: Latin American word choice (computadora, falla).
170
+
171
+ See `humanus/packs/NOTICE.md` for sources and licenses.
172
+ - **Classifier** (TypeSafe Jev, zero-shot): one request per segment carries
173
+ every question that applies to it; sentences are judged with their paragraph
174
+ as context. Answers are cached by content, so a rescan only asks about changed
175
+ text. `options.classifier` sets the model, concurrency, an input-token budget,
176
+ a deadline (to stay inside an API timeout) and few-shot examples from past
177
+ decisions. Without a decision model the lane reports `unavailable` and rules
178
+ findings are still returned.
179
+
180
+ Editors' decisions improve the classifier: `calibrate` fits Platt scaling (from
181
+ 10 labels) or isotonic regression (from 50) per detector; store it on the
182
+ detector as `calibration` and thresholds apply to the calibrated probability.
183
+
184
+ ## HTML usability
185
+
186
+ A separate, read-only scan for rendered HTML pages (contrast, tap targets, focus
187
+ visibility, alt text, generic AI-page patterns):
188
+
189
+ ```bash
190
+ humanus usability scan --page page.html --profile usability-profile.yml
191
+ ```
192
+
193
+ ## For agents
194
+
195
+ `humanus skill` prints an Agent Skill describing the check → suggest → choose
196
+ workflow.
197
+
198
+ ## Testing
199
+
200
+ Specifications are Gherkin, run with [Behave](https://behave.readthedocs.io/);
201
+ unit tests use pytest. Neither needs network access or API keys.
202
+
203
+ ```bash
204
+ behave
205
+ python -m pytest tests -q
206
+ ```
207
+
208
+ ## License
209
+
210
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,57 @@
1
+ [project]
2
+ name = "humanus"
3
+ version = "0.35.0"
4
+ description = "Configurable editorial compliance checks: policy packs, deterministic rules, zero-shot decision models and rewrite suggestions."
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ requires-python = ">=3.10"
8
+ authors = [
9
+ { name = "Anthus AI Solutions" }
10
+ ]
11
+ dependencies = [
12
+ "PyYAML>=6.0.2,<7.0.0",
13
+ "jsonschema>=4.18,<5",
14
+ "markdown-it-py>=3.0,<5",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ jev = [
19
+ "typesafe-sdk>=0.7.2,<0.8",
20
+ ]
21
+ dev = [
22
+ "behave>=1.2.6",
23
+ "pytest>=8.0.0",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/AnthusAI/Humanus"
28
+ Repository = "https://github.com/AnthusAI/Humanus"
29
+
30
+ [project.scripts]
31
+ humanus = "humanus.cli:main"
32
+
33
+ [build-system]
34
+ requires = ["setuptools>=68", "wheel"]
35
+ build-backend = "setuptools.build_meta"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.setuptools.package-data]
41
+ humanus = ["data/SKILL.md", "schemas/*.json", "packs/*.yml", "packs/*.md", "languages/*.yml"]
42
+
43
+ [tool.semantic_release]
44
+ version_toml = ["pyproject.toml:project.version"]
45
+ version_variables = ["src/humanus/__init__.py:__version__"]
46
+ build_command = "python -m pip install build && python -m build"
47
+ commit_parser = "conventional"
48
+ major_on_zero = false
49
+
50
+ [tool.semantic_release.branches.main]
51
+ match = "main"
52
+
53
+ [tool.semantic_release.changelog]
54
+ exclude_commit_patterns = []
55
+
56
+ [tool.semantic_release.remote]
57
+ type = "github"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,51 @@
1
+ """Humanus: a configurable editorial compliance engine.
2
+
3
+ A caller sends content and a policy pack; ``check`` returns where the content
4
+ breaks the pack's policies, ``suggest`` offers rewrites for a finding or any
5
+ selection, ``draft_questions`` helps write classifier detectors, and
6
+ ``calibrate`` learns from editors' decisions. Every request states its
7
+ content's language, which a shipped language pack must serve. The JSON Schemas under
8
+ ``humanus/schemas`` are the contract (``load_schema``).
9
+ """
10
+
11
+ __version__ = "0.35.0"
12
+
13
+ from .calibration import apply_calibration, calibrate
14
+ from .check import CheckRequestValidationError, check
15
+ from .contracts import ContractValidationError, load_schema, validate_payload
16
+ from .finding_ids import finding_id
17
+ from .languages import Language, UnsupportedLanguageError, list_languages, resolve_language
18
+ from .packs import builtin_pack, list_builtin_packs
19
+ from .policy_pack import PolicyPack, PolicyPackValidationError, load_policy_pack
20
+ from .segmentation import SegmentedDocument, segment_document
21
+ from .style_profile_import import StyleProfileImportError, policy_pack_from_style_profile
22
+ from .suggest import SuggestRequestValidationError, SuggestUnavailableError, draft_questions, suggest
23
+
24
+ __all__ = [
25
+ "CheckRequestValidationError",
26
+ "ContractValidationError",
27
+ "Language",
28
+ "PolicyPack",
29
+ "PolicyPackValidationError",
30
+ "SegmentedDocument",
31
+ "StyleProfileImportError",
32
+ "SuggestRequestValidationError",
33
+ "SuggestUnavailableError",
34
+ "UnsupportedLanguageError",
35
+ "__version__",
36
+ "apply_calibration",
37
+ "builtin_pack",
38
+ "calibrate",
39
+ "check",
40
+ "draft_questions",
41
+ "finding_id",
42
+ "list_builtin_packs",
43
+ "list_languages",
44
+ "load_policy_pack",
45
+ "load_schema",
46
+ "policy_pack_from_style_profile",
47
+ "resolve_language",
48
+ "segment_document",
49
+ "suggest",
50
+ "validate_payload",
51
+ ]
@@ -0,0 +1,8 @@
1
+ """Run ``python -m humanus`` the same way as the ``humanus`` console script."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ raise SystemExit(main())
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from typing import Any
6
+
7
+
8
+ def hash_stable(value: Any) -> str:
9
+ if isinstance(value, str):
10
+ payload = value
11
+ else:
12
+ payload = json.dumps(value, sort_keys=True, separators=(",", ":"), default=str)
13
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
14
+
15
+
16
+ def hash_short(value: Any) -> str:
17
+ return hash_stable(value)[:16]
18
+