Spaces:
Running
Running
unknown
Claude Opus 5.5
Merge the Space's 8-9 October work (origin/main) into the local changes
eaf69a7 Download pubrules.py from Rasheed/Test_AI_writing: direct link, hf CLI and curl.
- Browser
- Download file 15 kB
-
https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/pubrules.py
- Command line
-
hf download hf://spaces/Rasheed/Test_AI_writing/pubrules.py
-
curl -L -o pubrules.py https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/pubrules.py
15 kB
| """ | |
| 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.""" | |