Spaces:
Running
Running
File size: 14,986 Bytes
593e4c1 585f232 593e4c1 585f232 593e4c1 689da70 593e4c1 585f232 593e4c1 78dbb12 593e4c1 eaf69a7 593e4c1 eaf69a7 593e4c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | """
Publication English rulebook: the rules every humanized sentence must satisfy before it can replace the author's
sentence. One source of truth, used in two ways:
* PROMPT - the rules as instructions for the language-model steps (quality check, final polish, alternatives),
so the rewrite is written to the rules in the first place;
* violations(original, rewrite) - the automatic checks of every rule that can be measured. The clarity gate
(clarity.py) rejects a rewrite that breaks any of them; if no version of a sentence passes, the
author's original sentence is kept. A sentence is therefore never made worse than the author wrote it.
Rule groups
A. Integrity (never broken) - meaning, claims, numbers, units, citations, terms, acronyms, negation, direction of
every comparison, strength of every claim (hedging), one sentence in = one out
B. Grammar - complete, correct sentences (checked by the grammar / fluency models in clarity.py)
C. Academic register - formal, impersonal, no contractions, no "you", no first person singular, no
informal words, no rhetorical questions, no exclamation marks
D. Human academic style - no AI-typical vocabulary or stock phrases, no em dashes, no padding, plain
specific verbs, varied rhythm, no sentence-initial "And/But/So/Also"
E. Flow - logical links only where needed, no new paragraph-level claims
F. Consistency - the author's spelling (British / American), terminology, tense and number format
Space variables
PUBRULES 1 (default) | 0 = automatic rule checks off (the prompt rules stay)
PUB_MAX_WORDS default 40 a rewrite may not exceed this length unless the original already did
"""
import os
import re
import textparse as TP
ENABLED = os.environ.get("PUBRULES", "1") != "0"
MAX_WORDS = int(os.environ.get("PUB_MAX_WORDS", "40"))
# ------------------------------------------------------------------------------------------------ word lists
AI_WORDS = ["delve", "delves", "delving", "crucial", "pivotal", "comprehensive", "furthermore", "moreover",
"notably", "additionally", "leverage", "leverages", "leveraging", "robust", "seamless", "seamlessly",
"intricate", "landscape", "realm", "underscore", "underscores", "underscoring", "showcase", "showcases",
"tapestry", "multifaceted", "paramount", "meticulous", "meticulously", "holistic", "foster", "fosters",
"fostering", "testament", "ever-evolving", "groundbreaking", "cutting-edge", "game-changer",
"revolutionize", "revolutionise", "unprecedented", "invaluable", "noteworthy", "commendable",
"vibrant", "embark", "embarks", "unveil", "unveils", "harness", "harnesses", "harnessing",
"navigate", "navigating", "elevate", "elevates", "streamline", "synergy", "synergistic",
"nuanced", "myriad", "plethora", "endeavor", "endeavour", "bolster", "bolsters", "spearhead",
"transformative", "unwavering", "intrinsically", "profound", "profoundly", "enhance", "enhances"]
AI_PHRASES = [r"it is (?:important|worth|crucial|essential) to (?:note|mention|highlight|emphasi[sz]e)",
r"plays? an? (?:crucial|pivotal|vital|key|significant|important) role",
r"in today'?s (?:world|era|landscape|society|digital age)", r"a testament to",
r"in the (?:realm|landscape|ever-evolving|rapidly evolving) (?:of )?", r"shed(?:s|ding)? light on",
r"pav(?:e|es|ed|ing) the way", r"\bnot only\b.{1,80}\bbut also\b", r"at the end of the day",
r"in conclusion,", r"to sum up", r"it can be (?:seen|said|argued) that", r"serves? as a",
r"stands? as a", r"a wide (?:range|array|variety) of", r"the (?:intricate|complex) interplay",
r"in order to", r"due to the fact that", r"with regard to", r"a (?:key|vital|crucial) aspect",
r"rich (?:history|tapestry)", r"(?:navigate|navigating) the (?:complexities|challenges)"]
_AI_PHRASE_RE = [re.compile(p, re.I) for p in AI_PHRASES]
_AI_WORD_RE = re.compile(r"\b(" + "|".join(re.escape(w) for w in AI_WORDS) + r")\b", re.I)
NEGATORS = r"\b(?:not|no|never|none|neither|nor|without|cannot|unable|fail(?:s|ed)? to|lack(?:s|ed|ing)?|absence)\b|n't\b"
# ("indicate" is a neutral reporting verb, like "show": swapping the two does not change the strength of a claim)
HEDGES = r"\b(?:may|might|could|can|suggests?|suggested|suggesting|impl(?:y|ies|ied|ying)|appears?|appeared|" \
r"seems?|seemed|likely|unlikely|possibly|potentially|probably|perhaps|presumably|apparently|arguably|" \
r"approximately|roughly|nearly|almost|partly|partially|somewhat|tends?|tended|tending|inclined|" \
r"relatively|generally|typically|usually|often)\b"
BOOSTERS = r"\b(?:proves?|proved|proven|clearly|definitely|certainly|undoubtedly|always|completely|fully|" \
r"guarantees?|guaranteed|conclusively|obviously|absolutely|entirely|perfectly)\b"
# a rewrite may not state a cause the author did not state
CAUSAL = r"\b(?:because|since|therefore|thus|hence|consequently|thereby|as a result|due to|owing to|so that)\b"
_WE = re.compile(r"\b(?:we|our|ours|us)\b", re.I)
# direction words: a rewrite may not turn one into its opposite
OPPOSITES = [("increas", "decreas"), ("higher", "lower"), ("more", "less"), ("more", "fewer"), ("rise", "fall"),
("rais", "lower"), ("improv", "degrad"), ("improv", "wors"), ("above", "below"), ("maxim", "minim"),
("positive", "negative"), ("significant", "insignificant"), ("significant", "non-significant"),
("accept", "reject"), ("before", "after"), ("includ", "exclud"), ("enabl", "disabl"),
("strengthen", "weaken"), ("larger", "smaller"), ("gain", "loss"), ("success", "failure"),
("outperform", "underperform"), ("strong", "weak"), ("faster", "slower"), ("best", "worst"),
("upper", "lower"), ("tamper-evident", "tampered"), ("halve", "double"), ("allow", "block"), ("allow", "prevent"), ("permit", "forbid"), ("grant", "revok"),
("inside", "outside"), ("honest", "dishonest"), ("trust", "distrust"), ("safe", "unsafe")]
_INFORMAL_START = re.compile(r"^\s*(?:And|But|So|Also|Plus|Anyway|Well|Basically|Actually)\b")
_EM_DASH = re.compile(r"\s[—–]\s|—")
_CONTRACTION = re.compile(r"\b\w+(?:n't|'re|'ll|'ve|'m|'d)\b", re.I)
_FIRST_SING = re.compile(r"\b(?:I|me|my|mine|myself)\b")
_YOU = re.compile(r"\b(?:you|your|yours|yourself)\b", re.I)
_UNITS = (r"(?:%|ms|µs|μs|ns|s|Hz|kHz|MHz|GHz|dB|dBm|nm|µm|μm|mm|cm|m|km|µg|μg|ng|mg|g|kg|µL|μL|mL|ml|L|mV|kV|V|µA|μA|mA|A|"
r"mW|kW|MW|W|kWh|Wh|J|kJ|K|°C|°F|h|min|px|KB|MB|GB|TB|bps|Mbps|Gbps|Pa|kPa|MPa|GPa|bar|kN|mol|mmol|µmol|"
r"μmol|rpm)(?:/[A-Za-zµμ]+\d?)?")
_NUM = re.compile(rf"(?:(?<=[\s(])[-−+±])?(?<![\w.])\d+(?:[.,]\d+)*(?:\s?{_UNITS}(?![A-Za-z]))?")
_PAST_AUX = re.compile(r"\b(?:was|were|had|did)\b", re.I)
_PRES_AUX = re.compile(r"\b(?:is|are|has|have|does|do)\b", re.I)
def _count(pat, t):
return len(re.findall(pat, t or "", re.I))
def _stems(t):
return [w.lower() for w in re.findall(r"[A-Za-z][A-Za-z\-]+", t or "")]
def _has(words, stem):
return any(w.startswith(stem) for w in words)
def _acronyms(t):
return {w for w in re.findall(r"\b[A-Z][A-Z0-9]{1,}[a-z]?\b", TP.CITE.sub(" ", t or ""))}
def _numbers(t):
return sorted(re.sub(r"\s+", "", n).replace("−", "-") for n in _NUM.findall(TP.CITE.sub(" ", t or "")))
def violations(original, new):
"""Rules of groups A, C, D and F that `new` breaks compared with `original` (empty list = passes).
A rule only counts when the rewrite introduces the problem: whatever the author already wrote is never held
against the rewrite."""
if not ENABLED or not new or not original or new.strip() == original.strip():
return []
o, n = original, new
out = []
# ---- A. integrity
if _numbers(n) != _numbers(o):
out.append("numbers or units changed")
if _acronyms(o) - _acronyms(n):
out.append("acronym dropped")
if _acronyms(n) - _acronyms(o):
out.append("acronym added")
if (_count(NEGATORS, n) > 0) != (_count(NEGATORS, o) > 0) or abs(_count(NEGATORS, n) - _count(NEGATORS, o)) > 1:
out.append("negation changed")
ow, nw = _stems(o), _stems(n)
for a, b in OPPOSITES:
for x, y in ((a, b), (b, a)):
if _has(nw, x) and not _has(ow, x) and _has(ow, y) and not _has(nw, y):
out.append(f"direction reversed ({y}… → {x}…)")
break
if _count(HEDGES, o) and not _count(HEDGES, n):
out.append("hedging removed (claim made stronger)")
if _count(BOOSTERS, n) > _count(BOOSTERS, o):
out.append("claim made stronger")
if _count(CAUSAL, n) and not _count(CAUSAL, o): # "X mediates Y, suggesting Z" is not "because X, Z"
out.append("causal link added")
if _WE.search(n) and not _WE.search(o): # "we surveyed": a voice (and often a method) not the author's
out.append("first person plural added")
if len(TP.split_sentences(n)) != len(TP.split_sentences(o)): # one sentence in = one sentence out
out.append("sentence split or merged")
import profile_settings as PS
if PS.get("rules") == "integrity": # priority "ai": integrity rules only
return out
# ---- C. academic register
for pat, why in ((_CONTRACTION, "contraction"), (_FIRST_SING, "first person singular"), (_YOU, "addresses the reader")):
if pat.search(n) and not pat.search(o):
out.append(why)
if "!" in n and "!" not in o:
out.append("exclamation mark")
# ---- D. human academic style
added = {m.group(0).lower() for m in _AI_WORD_RE.finditer(n)} - {m.group(0).lower() for m in _AI_WORD_RE.finditer(o)}
if added:
out.append("AI-typical word: " + ", ".join(sorted(added)))
for rx in _AI_PHRASE_RE:
if rx.search(n) and not rx.search(o):
out.append("AI-typical phrase")
break
if _EM_DASH.search(n) and not _EM_DASH.search(o):
out.append("em dash")
if _INFORMAL_START.search(n) and not _INFORMAL_START.search(o):
out.append("sentence starts with a conjunction")
if len(n.split()) > max(MAX_WORDS, len(o.split()) + 5):
out.append("sentence too long")
# ---- F. consistency
po, pr_o = len(_PAST_AUX.findall(o)), len(_PRES_AUX.findall(o))
pn, pr_n = len(_PAST_AUX.findall(n)), len(_PRES_AUX.findall(n))
if (po and not pr_o and pr_n and not pn) or (pr_o and not po and pn and not pr_n):
out.append("tense changed")
return out
# ------------------------------------------------------------------------------------------------ prompt rules
PROMPT = f"""PUBLICATION ENGLISH RULES (every sentence you return must satisfy all of them):
A. Integrity - never broken:
A1. Same meaning, same claims, same scope. Add nothing (no new facts, examples, causes, names, numbers, results).
Drop nothing (every clause, condition, qualifier and list item stays).
A2. Every number, unit, statistic (β, p, r, R², %, n, CI), variable, symbol, equation reference, technical term,
acronym, model or dataset name, author name, year and citation ([12], (Kumar et al., 2021)) stays exactly as
written and stays attached to the same claim. Never move, merge, renumber or duplicate a citation.
A3. Never reverse a direction or a polarity: higher/lower, increase/decrease, before/after, allow/block, with/
without, not/no stay as they are. "tamper-evident" never becomes "can be tampered with".
A4. Keep the strength of every claim: keep may/might/suggests/approximately; never add proves/clearly/always/all.
A5. One sentence in, one sentence out: never split, merge, drop or reorder sentences.
A6. Never replace a technical term with a synonym ("accuracy" is not "precision", "sweeps" is not "reaches",
"escalates" is not "increases"); when unsure, keep the author's word. The study's own constructs (every term
defined with an abbreviation, e.g. "travel satisfaction (TS)", "intimacy (IN)") keep their exact names: never
"pleasure", "contentment", "happiness", "closeness", "allegiance", "devotion" for them.
A7. Never add a reason, cause, explanation or attribution the original does not state ("according to some
experts", "since they have ...", "a systematic analysis found", "researchers contend").
A8. Keep every list item and every name or place ("Digha, Darjeeling, Jhargram and Purulia" keeps all four).
A9. Keep statistics exactly: "p = .120" stays "p = .120"; never turn "=" into "<" or ">", never add an operator
that is missing in the original, never drop one ("p < .001" never becomes "p .001").
A10. Never end a sentence or a term half-way ("... and to enhance their.", "well-,").
B. Grammar - every sentence complete and correct: subject-verb agreement, articles, verb forms, tense, prepositions,
punctuation (keep every comma of the original that is still needed: in lists, after introductory phrases such as
"After COVID-19," or "However,", and after a bracketed abbreviation in a list; never remove commas to change the
style), no missing, doubled or run-together words, no
fragments, no run-on sentences, a capital only where English needs one ("Greshake et al. generalised", not
"et al. Generalised").
C. Academic register: formal and impersonal; no contractions, no "you", no first person singular, no informal or
conversational words, no rhetorical questions, no exclamation marks; "we" only where the author used it.
D. Human academic style (how careful researchers actually write):
D1. Plain, specific verbs (shows, reduces, uses, compares) instead of inflated ones.
D2. Never use: {", ".join(AI_WORDS[:40])}; never use stock phrases such as "it is important to note",
"plays a crucial role", "in today's world", "a testament to", "sheds light on", "paves the way",
"not only ... but also", "a wide range of", "in order to", "due to the fact that", "serves as".
D3. No em dashes, no lists of three adjectives, no padding, no summary sentences that repeat the previous one.
D4. Vary rhythm naturally (a shorter direct sentence after a long one is good); never above {MAX_WORDS} words
unless the original sentence already was; never start with And, But, So or Also.
E. Flow: each sentence follows logically from the previous one; add a linking word (however, therefore, in
contrast, for example) only where the logic needs it; never add a new claim to make text flow.
F. Consistency: keep the author's spelling (British or American), terminology, tense and number format throughout;
the same concept always has the same name."""
|