| """Deterministic post-edits that push prose toward human statistical patterns.
|
|
|
| Detectors often score even sentence length, stock transitions, and Latinate diction.
|
| These transforms keep meaning while breaking that smoothness.
|
| """
|
|
|
| from __future__ import annotations
|
|
|
| import random
|
| import re
|
|
|
|
|
|
|
| def _rng_for(text: str) -> random.Random:
|
| return random.Random(hash(text) & 0xFFFFFFFF)
|
|
|
|
|
| PHRASE_SWAPS: list[tuple[re.Pattern[str], str]] = [
|
| (re.compile(r"\bIt is important to note that\b", re.I), ""),
|
| (re.compile(r"\bIt is worth noting that\b", re.I), ""),
|
| (re.compile(r"\bIt should be noted that\b", re.I), ""),
|
| (re.compile(r"\bIn today's (?:fast-paced |ever-evolving )?world,?\s*", re.I), ""),
|
| (re.compile(r"\bIn conclusion,?\s*", re.I), ""),
|
| (re.compile(r"\bTo summarize,?\s*", re.I), ""),
|
| (re.compile(r"\bOverall,?\s*", re.I), ""),
|
| (re.compile(r"\bFurthermore,?\s*", re.I), ""),
|
| (re.compile(r"\bMoreover,?\s*", re.I), ""),
|
| (re.compile(r"\bAdditionally,?\s*", re.I), "Also, "),
|
| (re.compile(r"\bConsequently,?\s*", re.I), "So "),
|
| (re.compile(r"\bThus,?\s*", re.I), "So "),
|
| (re.compile(r"\bTherefore,?\s*", re.I), "So "),
|
| (re.compile(r"\bIn order to\b", re.I), "to"),
|
| (re.compile(r"\bdue to the fact that\b", re.I), "because"),
|
| (re.compile(r"\ba large number of\b", re.I), "many"),
|
| (re.compile(r"\ba myriad of\b", re.I), "many"),
|
| (re.compile(r"\butilize\b", re.I), "use"),
|
| (re.compile(r"\butilizes\b", re.I), "uses"),
|
| (re.compile(r"\butilized\b", re.I), "used"),
|
| (re.compile(r"\butilizing\b", re.I), "using"),
|
| (re.compile(r"\bfacilitate\b", re.I), "help"),
|
| (re.compile(r"\bfacilitates\b", re.I), "helps"),
|
| (re.compile(r"\bfacilitated\b", re.I), "helped"),
|
| (re.compile(r"\bfacilitating\b", re.I), "helping"),
|
| (re.compile(r"\bimplementation of\b", re.I), "putting in place "),
|
| (re.compile(r"\bimplementations\b", re.I), "rollouts"),
|
| (re.compile(r"\bimplementation\b", re.I), "rollout"),
|
| (re.compile(r"\bimplement\b", re.I), "roll out"),
|
| (re.compile(r"\bimplements\b", re.I), "rolls out"),
|
| (re.compile(r"\bimplemented\b", re.I), "rolled out"),
|
| (re.compile(r"\bimplementing\b", re.I), "rolling out"),
|
| (re.compile(r"\bleverage\b", re.I), "use"),
|
| (re.compile(r"\bleverages\b", re.I), "uses"),
|
| (re.compile(r"\bleveraged\b", re.I), "used"),
|
| (re.compile(r"\bleveraging\b", re.I), "using"),
|
| (re.compile(r"\bharness\b", re.I), "use"),
|
| (re.compile(r"\bharnesses\b", re.I), "uses"),
|
| (re.compile(r"\bharnessed\b", re.I), "used"),
|
| (re.compile(r"\bharnessing\b", re.I), "using"),
|
| (re.compile(r"\brobust\b", re.I), "solid"),
|
| (re.compile(r"\bseamless(?:ly)?\b", re.I), "smooth"),
|
| (re.compile(r"\bpivotal\b", re.I), "important"),
|
| (re.compile(r"\bcomprehensive\b", re.I), "full"),
|
| (re.compile(r"\bmultifaceted\b", re.I), "varied"),
|
| (re.compile(r"\bcutting-edge\b", re.I), "new"),
|
| (re.compile(r"\bgame-?changer\b", re.I), "big shift"),
|
| (re.compile(r"\bdelve(?:s|d)? into\b", re.I), "look at"),
|
| (re.compile(r"\bdive(?:s|d)? into\b", re.I), "look at"),
|
| (re.compile(r"\blandscape\b", re.I), "field"),
|
| (re.compile(r"\brealm\b", re.I), "area"),
|
| (re.compile(r"\btapestry\b", re.I), "mix"),
|
| (re.compile(r"\bembark(?:s|ed)? on\b", re.I), "start"),
|
| (re.compile(r"\bunderscores\b", re.I), "shows"),
|
| (re.compile(r"\bshowcases\b", re.I), "shows"),
|
| (re.compile(r"\bplays a crucial role in\b", re.I), "matters for"),
|
| (re.compile(r"\bserves as a testament to\b", re.I), "shows"),
|
| (re.compile(r"\bwhen it comes to\b", re.I), "for"),
|
| (re.compile(r"\bat the end of the day,?\s*", re.I), ""),
|
| (re.compile(r"\bin the ever-evolving\b", re.I), "in the changing"),
|
| (re.compile(r"\bnot only\s+(.+?)\s+but also\s+", re.I), r"\1 and "),
|
| ]
|
|
|
| CONTRACTIONS = [
|
| (re.compile(r"\bdo not\b"), "don't"),
|
| (re.compile(r"\bdoes not\b"), "doesn't"),
|
| (re.compile(r"\bdid not\b"), "didn't"),
|
| (re.compile(r"\bis not\b"), "isn't"),
|
| (re.compile(r"\bare not\b"), "aren't"),
|
| (re.compile(r"\bwas not\b"), "wasn't"),
|
| (re.compile(r"\bwere not\b"), "weren't"),
|
| (re.compile(r"\bwill not\b"), "won't"),
|
| (re.compile(r"\bcannot\b"), "can't"),
|
| (re.compile(r"\bcould not\b"), "couldn't"),
|
| (re.compile(r"\bwould not\b"), "wouldn't"),
|
| (re.compile(r"\bshould not\b"), "shouldn't"),
|
| (re.compile(r"\bit is\b"), "it's"),
|
| (re.compile(r"\bthat is\b"), "that's"),
|
| (re.compile(r"\bthere is\b"), "there's"),
|
| (re.compile(r"\bwe are\b"), "we're"),
|
| (re.compile(r"\bwe have\b"), "we've"),
|
| (re.compile(r"\byou are\b"), "you're"),
|
| (re.compile(r"\bthey are\b"), "they're"),
|
| (re.compile(r"\bI am\b"), "I'm"),
|
| ]
|
|
|
|
|
| def apply_phrase_swaps(text: str) -> str:
|
| out = text
|
| for pattern, repl in PHRASE_SWAPS:
|
| out = pattern.sub(repl, out)
|
|
|
| out = re.sub(r" {2,}", " ", out)
|
| out = re.sub(r"\s+([,.;:])", r"\1", out)
|
| out = re.sub(r"([.!?])\s*([a-z])", lambda m: f"{m.group(1)} {m.group(2).upper()}", out)
|
| return out
|
|
|
|
|
| def apply_contractions(text: str, tone: str) -> str:
|
| if tone == "Formal":
|
|
|
| pairs = CONTRACTIONS[:8]
|
| else:
|
| pairs = CONTRACTIONS
|
| out = text
|
| for pattern, repl in pairs:
|
| out = pattern.sub(repl, out)
|
| return out
|
|
|
|
|
| def _split_sentences(paragraph: str) -> list[str]:
|
| parts = re.split(r"(?<=[.!?])\s+", paragraph.strip())
|
| return [p.strip() for p in parts if p.strip()]
|
|
|
|
|
| def burst_paragraph(paragraph: str, rng: random.Random) -> str:
|
| """Break uniform mid-length sentences; occasionally isolate a short line."""
|
| sentences = _split_sentences(paragraph)
|
| if len(sentences) < 2:
|
| return paragraph.strip()
|
|
|
| rebuilt: list[str] = []
|
| for sent in sentences:
|
| words = sent.split()
|
|
|
| if len(words) >= 22 and "," in sent and rng.random() < 0.55:
|
| left, right = sent.split(",", 1)
|
| left = left.strip()
|
| right = right.strip()
|
| if right and right[0].islower():
|
| right = right[0].upper() + right[1:]
|
| if not left.endswith((".", "!", "?")):
|
| left = left + "."
|
| rebuilt.append(left)
|
| rebuilt.append(right if right.endswith((".", "!", "?")) else right + ".")
|
| else:
|
| rebuilt.append(sent)
|
|
|
|
|
| pieces: list[str] = []
|
| buf: list[str] = []
|
| for sent in rebuilt:
|
| w = len(sent.split())
|
| if w <= 8 and rng.random() < 0.35 and buf:
|
| pieces.append(" ".join(buf))
|
| pieces.append(sent)
|
| buf = []
|
| else:
|
| buf.append(sent)
|
| if buf:
|
| pieces.append(" ".join(buf))
|
| return "\n\n".join(pieces)
|
|
|
|
|
| def stagger_openers(text: str, rng: random.Random) -> str:
|
| """Nudge a few paragraph openings away from tidy topic-sentence feel."""
|
| openers = ["Still,", "And", "But", "Look,", "That said,", "Honestly,"]
|
| paras = [p.strip() for p in re.split(r"\n\s*\n", text) if p.strip()]
|
| if len(paras) < 2:
|
| return text
|
|
|
| out: list[str] = []
|
| for i, para in enumerate(paras):
|
| if i == 0 or rng.random() > 0.28:
|
| out.append(para)
|
| continue
|
| first = para.split(" ", 1)
|
|
|
| if para.split(",", 1)[0] in {o.rstrip(",") for o in openers}:
|
| out.append(para)
|
| continue
|
| if rng.random() < 0.5 and len(para.split()) > 6:
|
| opener = rng.choice(openers)
|
| rest = para[0].lower() + para[1:] if para and para[0].isupper() else para
|
|
|
| if opener in {"And", "But"}:
|
| out.append(f"{opener} {rest}")
|
| else:
|
| out.append(f"{opener} {rest}")
|
| else:
|
| out.append(para)
|
| return "\n\n".join(out)
|
|
|
|
|
| def tidy_artifacts(text: str) -> str:
|
| text = re.sub(r"\n{3,}", "\n\n", text)
|
| text = re.sub(r" {2,}", " ", text)
|
|
|
| text = re.sub(r",\s*,", ",", text)
|
| text = re.sub(r"^\s*,\s*", "", text, flags=re.M)
|
| lines = []
|
| for para in text.split("\n"):
|
| lines.append(para.strip() if para.strip() else "")
|
| return "\n".join(lines).strip()
|
|
|
|
|
| def mechanical_humanize(text: str, tone: str = "Neutral") -> str:
|
| """Apply non-LLM humanizing transforms."""
|
| if not text.strip():
|
| return text
|
| rng = _rng_for(text)
|
| out = apply_phrase_swaps(text)
|
| out = apply_contractions(out, tone)
|
|
|
| paras = [p.strip() for p in re.split(r"\n\s*\n", out) if p.strip()]
|
| bursted = [burst_paragraph(p, rng) for p in paras]
|
|
|
| flat = "\n\n".join(bursted)
|
| flat = stagger_openers(flat, rng)
|
| return tidy_artifacts(flat)
|
|
|