Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,222 +3,274 @@ import random
|
|
| 3 |
import re
|
| 4 |
import nltk
|
| 5 |
from nltk.tokenize import sent_tokenize, word_tokenize
|
| 6 |
-
from nltk.corpus import stopwords
|
| 7 |
-
from nltk.tag import pos_tag
|
| 8 |
import string
|
| 9 |
from textstat import flesch_reading_ease, flesch_kincaid_grade
|
| 10 |
-
import spacy
|
| 11 |
-
from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
|
| 12 |
-
import torch
|
| 13 |
|
| 14 |
# Download required NLTK data
|
| 15 |
try:
|
| 16 |
nltk.download('punkt', quiet=True)
|
| 17 |
nltk.download('averaged_perceptron_tagger', quiet=True)
|
| 18 |
nltk.download('stopwords', quiet=True)
|
| 19 |
-
|
| 20 |
-
except:
|
| 21 |
-
|
| 22 |
|
| 23 |
class AIContentHumanizer:
|
| 24 |
def __init__(self):
|
| 25 |
-
self.setup_models()
|
| 26 |
self.setup_humanization_patterns()
|
| 27 |
|
| 28 |
-
def setup_models(self):
|
| 29 |
-
"""Initialize AI detection and paraphrasing models"""
|
| 30 |
-
try:
|
| 31 |
-
# Load spaCy model
|
| 32 |
-
self.nlp = spacy.load("en_core_web_sm")
|
| 33 |
-
except:
|
| 34 |
-
# Fallback if spaCy model not available
|
| 35 |
-
self.nlp = None
|
| 36 |
-
|
| 37 |
-
# Paraphrasing patterns and synonyms
|
| 38 |
-
self.synonyms = {
|
| 39 |
-
'however': ['but', 'yet', 'though', 'nevertheless', 'still'],
|
| 40 |
-
'therefore': ['so', 'thus', 'hence', 'as a result', 'consequently'],
|
| 41 |
-
'furthermore': ['also', 'moreover', 'additionally', 'besides', 'plus'],
|
| 42 |
-
'nevertheless': ['however', 'yet', 'still', 'but', 'though'],
|
| 43 |
-
'consequently': ['therefore', 'so', 'thus', 'as a result', 'hence'],
|
| 44 |
-
'significant': ['important', 'major', 'notable', 'considerable', 'substantial'],
|
| 45 |
-
'utilize': ['use', 'employ', 'apply', 'make use of', 'take advantage of'],
|
| 46 |
-
'demonstrate': ['show', 'prove', 'display', 'exhibit', 'reveal'],
|
| 47 |
-
'numerous': ['many', 'several', 'various', 'countless', 'multiple'],
|
| 48 |
-
'substantial': ['significant', 'considerable', 'large', 'major', 'important']
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
def setup_humanization_patterns(self):
|
| 52 |
"""Setup patterns for humanizing text"""
|
| 53 |
-
self.ai_phrases = [
|
| 54 |
-
r'\b(it is important to note that|it should be noted that|it is worth noting that)\b',
|
| 55 |
-
r'\b(in conclusion|to conclude|in summary|to summarize)\b',
|
| 56 |
-
r'\b(furthermore|moreover|additionally|in addition)\b',
|
| 57 |
-
r'\b(however|nevertheless|nonetheless|on the other hand)\b',
|
| 58 |
-
r'\b(therefore|consequently|as a result|thus)\b',
|
| 59 |
-
r'\b(various|numerous|several|multiple)\b',
|
| 60 |
-
r'\b(significant|substantial|considerable|notable)\b',
|
| 61 |
-
r'\b(utilize|implement|demonstrate|facilitate)\b'
|
| 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 |
-
def
|
| 90 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
sentences = sent_tokenize(text)
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
-
for sentence in sentences:
|
| 95 |
-
#
|
| 96 |
-
if random.random() < 0.3:
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
words = sentence.split()
|
| 100 |
-
if len(words) > 3:
|
| 101 |
-
insert_pos = random.randint(1, min(3, len(words)-1))
|
| 102 |
-
words.insert(insert_pos, filler)
|
| 103 |
-
sentence = ' '.join(words)
|
| 104 |
|
| 105 |
-
#
|
| 106 |
-
|
| 107 |
-
|
|
|
|
| 108 |
|
| 109 |
-
|
| 110 |
|
| 111 |
-
return ' '.join(
|
| 112 |
|
| 113 |
-
def
|
| 114 |
-
"""
|
| 115 |
sentences = sent_tokenize(text)
|
| 116 |
-
|
| 117 |
|
| 118 |
-
for
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
sentences[i+1] = "" # Skip next sentence
|
| 127 |
-
continue
|
| 128 |
|
| 129 |
-
|
| 130 |
-
varied_sentences.append(sentence)
|
| 131 |
|
| 132 |
-
return ' '.join(
|
| 133 |
|
| 134 |
-
def
|
| 135 |
-
"""
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
|
|
|
|
|
|
| 140 |
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
return
|
| 149 |
|
| 150 |
-
def
|
| 151 |
-
"""
|
| 152 |
-
# Replace some periods with commas for flow
|
| 153 |
sentences = sent_tokenize(text)
|
| 154 |
-
|
| 155 |
-
# Occasionally use semicolons
|
| 156 |
-
if random.random() < 0.3:
|
| 157 |
-
idx = random.randint(0, len(sentences)-2)
|
| 158 |
-
if len(sentences[idx].split()) > 6:
|
| 159 |
-
sentences[idx] = sentences[idx].rstrip('.') + ';'
|
| 160 |
-
sentences[idx+1] = sentences[idx+1][0].lower() + sentences[idx+1][1:]
|
| 161 |
-
|
| 162 |
-
return ' '.join(sentences)
|
| 163 |
-
|
| 164 |
-
def add_personal_touches(self, text):
|
| 165 |
-
"""Add personal touches and opinions"""
|
| 166 |
-
personal_phrases = [
|
| 167 |
-
"I think", "In my opinion", "From what I've seen", "Personally,",
|
| 168 |
-
"I believe", "It seems to me", "From my experience"
|
| 169 |
-
]
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
-
return ' '.join(
|
| 179 |
|
| 180 |
-
def
|
| 181 |
-
"""
|
| 182 |
-
|
| 183 |
-
|
| 184 |
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
synonym = random.choice(self.synonyms[lower_word])
|
| 189 |
-
new_words.append(synonym)
|
| 190 |
-
else:
|
| 191 |
-
new_words.append(word)
|
| 192 |
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
"""Main humanization function"""
|
| 197 |
-
if not text or not text.strip():
|
| 198 |
-
return "Please provide text to humanize."
|
| 199 |
|
| 200 |
-
|
| 201 |
-
text = text.strip()
|
| 202 |
|
| 203 |
-
|
| 204 |
-
if intensity == "light":
|
| 205 |
-
text = self.add_contractions(text)
|
| 206 |
-
text = self.replace_formal_words(text)
|
| 207 |
-
elif intensity == "medium":
|
| 208 |
-
text = self.replace_formal_words(text)
|
| 209 |
-
text = self.add_contractions(text)
|
| 210 |
-
text = self.paraphrase_with_synonyms(text)
|
| 211 |
-
text = self.vary_sentence_structure(text)
|
| 212 |
-
else: # heavy
|
| 213 |
-
text = self.replace_formal_words(text)
|
| 214 |
-
text = self.add_contractions(text)
|
| 215 |
-
text = self.paraphrase_with_synonyms(text)
|
| 216 |
-
text = self.vary_sentence_structure(text)
|
| 217 |
-
text = self.add_human_errors(text)
|
| 218 |
-
text = self.adjust_punctuation(text)
|
| 219 |
-
text = self.add_personal_touches(text)
|
| 220 |
-
|
| 221 |
-
return text
|
| 222 |
|
| 223 |
def get_readability_score(self, text):
|
| 224 |
"""Calculate readability metrics"""
|
|
@@ -242,71 +294,123 @@ class AIContentHumanizer:
|
|
| 242 |
level = "Very Difficult"
|
| 243 |
|
| 244 |
return f"Flesch Score: {flesch_score:.1f} ({level})\nGrade Level: {fk_grade:.1f}"
|
| 245 |
-
except:
|
| 246 |
-
return "Could not calculate readability"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
|
| 248 |
def create_interface():
|
| 249 |
humanizer = AIContentHumanizer()
|
| 250 |
|
| 251 |
def process_text(input_text, intensity):
|
| 252 |
if not input_text:
|
| 253 |
-
return "Please enter some text to humanize.", ""
|
| 254 |
-
|
| 255 |
-
humanized = humanizer.humanize_text(input_text, intensity)
|
| 256 |
-
readability = humanizer.get_readability_score(humanized)
|
| 257 |
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
|
| 260 |
# Custom CSS for better UI
|
| 261 |
css = """
|
| 262 |
.gradio-container {
|
| 263 |
font-family: 'Inter', sans-serif;
|
|
|
|
|
|
|
| 264 |
}
|
| 265 |
.main-header {
|
| 266 |
text-align: center;
|
| 267 |
margin-bottom: 30px;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
}
|
| 269 |
.feature-box {
|
| 270 |
border: 1px solid #e1e5e9;
|
| 271 |
-
border-radius:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
padding: 15px;
|
| 273 |
margin: 10px 0;
|
| 274 |
-
|
| 275 |
}
|
| 276 |
"""
|
| 277 |
|
| 278 |
-
with gr.Blocks(css=css, title="AI Content Humanizer") as interface:
|
| 279 |
gr.HTML("""
|
| 280 |
<div class="main-header">
|
| 281 |
<h1>π€β‘οΈπ€ AI Content Humanizer</h1>
|
| 282 |
-
<p>Transform AI-generated content into natural, human-like text</p>
|
|
|
|
| 283 |
</div>
|
| 284 |
""")
|
| 285 |
|
| 286 |
with gr.Row():
|
| 287 |
-
with gr.Column(scale=
|
| 288 |
input_text = gr.Textbox(
|
| 289 |
label="π Enter AI-generated text",
|
| 290 |
-
placeholder="Paste your AI-generated content here...",
|
| 291 |
-
lines=
|
| 292 |
-
max_lines=
|
| 293 |
)
|
| 294 |
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
|
|
|
| 301 |
|
| 302 |
-
humanize_btn = gr.Button(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 303 |
|
| 304 |
-
with gr.Column(scale=
|
| 305 |
output_text = gr.Textbox(
|
| 306 |
label="β
Humanized Text",
|
| 307 |
-
lines=
|
| 308 |
-
max_lines=
|
| 309 |
-
interactive=
|
|
|
|
| 310 |
)
|
| 311 |
|
| 312 |
readability_info = gr.Textbox(
|
|
@@ -315,33 +419,68 @@ def create_interface():
|
|
| 315 |
interactive=False
|
| 316 |
)
|
| 317 |
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
<
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
|
| 331 |
# Example texts
|
| 332 |
examples = [
|
| 333 |
-
[
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
]
|
| 337 |
|
| 338 |
gr.Examples(
|
| 339 |
examples=examples,
|
| 340 |
inputs=[input_text, intensity],
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
| 342 |
)
|
| 343 |
|
|
|
|
| 344 |
humanize_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
fn=process_text,
|
| 346 |
inputs=[input_text, intensity],
|
| 347 |
outputs=[output_text, readability_info]
|
|
@@ -350,5 +489,10 @@ def create_interface():
|
|
| 350 |
return interface
|
| 351 |
|
| 352 |
if __name__ == "__main__":
|
|
|
|
| 353 |
interface = create_interface()
|
| 354 |
-
interface.launch(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import re
|
| 4 |
import nltk
|
| 5 |
from nltk.tokenize import sent_tokenize, word_tokenize
|
|
|
|
|
|
|
| 6 |
import string
|
| 7 |
from textstat import flesch_reading_ease, flesch_kincaid_grade
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# Download required NLTK data
|
| 10 |
try:
|
| 11 |
nltk.download('punkt', quiet=True)
|
| 12 |
nltk.download('averaged_perceptron_tagger', quiet=True)
|
| 13 |
nltk.download('stopwords', quiet=True)
|
| 14 |
+
print("NLTK data downloaded successfully")
|
| 15 |
+
except Exception as e:
|
| 16 |
+
print(f"NLTK download error: {e}")
|
| 17 |
|
| 18 |
class AIContentHumanizer:
|
| 19 |
def __init__(self):
|
|
|
|
| 20 |
self.setup_humanization_patterns()
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
def setup_humanization_patterns(self):
|
| 23 |
"""Setup patterns for humanizing text"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
# AI-typical phrases and their human alternatives
|
| 26 |
+
self.ai_replacements = {
|
| 27 |
+
r'\bit is important to note that\b': [
|
| 28 |
+
"worth mentioning that", "keep in mind that", "note that",
|
| 29 |
+
"interestingly,", "what's notable is that", "by the way,"
|
| 30 |
+
],
|
| 31 |
+
r'\bit should be noted that\b': [
|
| 32 |
+
"remember that", "worth noting that", "keep in mind",
|
| 33 |
+
"importantly,", "note that", "just so you know,"
|
| 34 |
+
],
|
| 35 |
+
r'\bin conclusion\b': [
|
| 36 |
+
"to wrap up", "all in all", "bottom line",
|
| 37 |
+
"so basically", "in the end", "overall", "to sum it up"
|
| 38 |
+
],
|
| 39 |
+
r'\bto conclude\b': [
|
| 40 |
+
"to wrap up", "all in all", "in the end",
|
| 41 |
+
"so basically", "overall", "final thoughts"
|
| 42 |
+
],
|
| 43 |
+
r'\bfurthermore\b': [
|
| 44 |
+
"also", "plus", "what's more", "on top of that",
|
| 45 |
+
"and", "additionally", "besides", "another thing"
|
| 46 |
+
],
|
| 47 |
+
r'\bmoreover\b': [
|
| 48 |
+
"also", "plus", "and", "what's more",
|
| 49 |
+
"on top of that", "besides", "another thing"
|
| 50 |
+
],
|
| 51 |
+
r'\bhowever\b': [
|
| 52 |
+
"but", "though", "yet", "still", "although",
|
| 53 |
+
"on the flip side", "that said", "even so"
|
| 54 |
+
],
|
| 55 |
+
r'\btherefore\b': [
|
| 56 |
+
"so", "that's why", "which means", "as a result",
|
| 57 |
+
"this means", "hence", "because of this"
|
| 58 |
+
],
|
| 59 |
+
r'\bconsequently\b': [
|
| 60 |
+
"so", "as a result", "that's why", "this means",
|
| 61 |
+
"because of this", "hence", "due to this"
|
| 62 |
+
],
|
| 63 |
+
r'\bsignificant(?:ly)?\b': [
|
| 64 |
+
"big", "major", "important", "huge", "substantial",
|
| 65 |
+
"considerable", "notable", "really", "pretty"
|
| 66 |
+
],
|
| 67 |
+
r'\bnumerous\b': [
|
| 68 |
+
"many", "lots of", "plenty of", "tons of",
|
| 69 |
+
"countless", "several", "a bunch of"
|
| 70 |
+
],
|
| 71 |
+
r'\butilize\b': [
|
| 72 |
+
"use", "make use of", "work with", "employ",
|
| 73 |
+
"take advantage of", "go with"
|
| 74 |
+
],
|
| 75 |
+
r'\bdemonstrate\b': [
|
| 76 |
+
"show", "prove", "make clear", "illustrate",
|
| 77 |
+
"reveal", "display", "point out"
|
| 78 |
+
],
|
| 79 |
+
r'\bfacilitate\b': [
|
| 80 |
+
"help", "make easier", "enable", "assist",
|
| 81 |
+
"make possible", "support", "help with"
|
| 82 |
+
],
|
| 83 |
+
r'\bimplement\b': [
|
| 84 |
+
"put in place", "set up", "start using", "apply",
|
| 85 |
+
"carry out", "execute", "roll out"
|
| 86 |
+
],
|
| 87 |
+
r'\bvarious\b': [
|
| 88 |
+
"different", "several", "many", "a bunch of",
|
| 89 |
+
"multiple", "all sorts of"
|
| 90 |
+
],
|
| 91 |
+
r'\bsubstantial\b': [
|
| 92 |
+
"big", "major", "significant", "large",
|
| 93 |
+
"considerable", "huge", "pretty big"
|
| 94 |
+
]
|
| 95 |
}
|
| 96 |
|
| 97 |
+
# Contractions for natural speech
|
| 98 |
+
self.contractions = {
|
| 99 |
+
r'\bit is\b': "it's",
|
| 100 |
+
r'\bthat is\b': "that's",
|
| 101 |
+
r'\bwhat is\b': "what's",
|
| 102 |
+
r'\bwhere is\b': "where's",
|
| 103 |
+
r'\bwho is\b': "who's",
|
| 104 |
+
r'\bwe are\b': "we're",
|
| 105 |
+
r'\bthey are\b': "they're",
|
| 106 |
+
r'\byou are\b': "you're",
|
| 107 |
+
r'\bi am\b': "I'm",
|
| 108 |
+
r'\bhe is\b': "he's",
|
| 109 |
+
r'\bshe is\b': "she's",
|
| 110 |
+
r'\bwill not\b': "won't",
|
| 111 |
+
r'\bcannot\b': "can't",
|
| 112 |
+
r'\bdo not\b': "don't",
|
| 113 |
+
r'\bdoes not\b': "doesn't",
|
| 114 |
+
r'\bdid not\b': "didn't",
|
| 115 |
+
r'\bhave not\b': "haven't",
|
| 116 |
+
r'\bhas not\b': "hasn't",
|
| 117 |
+
r'\bhad not\b': "hadn't",
|
| 118 |
+
r'\bwould not\b': "wouldn't",
|
| 119 |
+
r'\bshould not\b': "shouldn't",
|
| 120 |
+
r'\bcould not\b': "couldn't",
|
| 121 |
+
r'\bis not\b': "isn't",
|
| 122 |
+
r'\bare not\b': "aren't",
|
| 123 |
+
r'\bwas not\b': "wasn't",
|
| 124 |
+
r'\bwere not\b': "weren't"
|
| 125 |
}
|
| 126 |
+
|
| 127 |
+
# Filler words and phrases humans use
|
| 128 |
+
self.human_fillers = [
|
| 129 |
+
'actually', 'basically', 'really', 'pretty much', 'kind of',
|
| 130 |
+
'sort of', 'you know', 'I mean', 'like', 'well',
|
| 131 |
+
'honestly', 'frankly', 'obviously', 'clearly'
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
# Opinion markers to make text more personal
|
| 135 |
+
self.opinion_markers = [
|
| 136 |
+
"I think", "I believe", "In my opinion", "From what I've seen",
|
| 137 |
+
"It seems to me", "I feel like", "My take is", "Personally,",
|
| 138 |
+
"From my experience", "I'd say", "I reckon", "I suspect"
|
| 139 |
+
]
|
| 140 |
+
|
| 141 |
+
# Casual sentence starters
|
| 142 |
+
self.casual_starters = [
|
| 143 |
+
"Look,", "Listen,", "Here's the thing:", "The way I see it,",
|
| 144 |
+
"To be honest,", "Frankly,", "Let me tell you,", "You know what?",
|
| 145 |
+
"The truth is,", "Here's what I think:"
|
| 146 |
+
]
|
| 147 |
|
| 148 |
+
def replace_ai_phrases(self, text):
|
| 149 |
+
"""Replace AI-typical phrases with human alternatives"""
|
| 150 |
+
for pattern, replacements in self.ai_replacements.items():
|
| 151 |
+
matches = re.finditer(pattern, text, re.IGNORECASE)
|
| 152 |
+
for match in reversed(list(matches)): # Reverse to maintain positions
|
| 153 |
+
replacement = random.choice(replacements)
|
| 154 |
+
start, end = match.span()
|
| 155 |
+
# Preserve original capitalization
|
| 156 |
+
if text[start].isupper():
|
| 157 |
+
replacement = replacement.capitalize()
|
| 158 |
+
text = text[:start] + replacement + text[end:]
|
| 159 |
+
return text
|
| 160 |
+
|
| 161 |
+
def add_contractions(self, text):
|
| 162 |
+
"""Add contractions for natural speech"""
|
| 163 |
+
for pattern, contraction in self.contractions.items():
|
| 164 |
+
text = re.sub(pattern, contraction, text, flags=re.IGNORECASE)
|
| 165 |
+
return text
|
| 166 |
+
|
| 167 |
+
def add_personal_touches(self, text):
|
| 168 |
+
"""Add personal opinions and touches"""
|
| 169 |
sentences = sent_tokenize(text)
|
| 170 |
+
if len(sentences) == 0:
|
| 171 |
+
return text
|
| 172 |
+
|
| 173 |
+
modified_sentences = []
|
| 174 |
|
| 175 |
+
for i, sentence in enumerate(sentences):
|
| 176 |
+
# Add opinion markers occasionally
|
| 177 |
+
if random.random() < 0.3 and len(sentence.split()) > 5:
|
| 178 |
+
opinion = random.choice(self.opinion_markers)
|
| 179 |
+
sentence = opinion + " " + sentence.lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
+
# Add casual starters occasionally to first sentence
|
| 182 |
+
elif random.random() < 0.2 and i == 0:
|
| 183 |
+
starter = random.choice(self.casual_starters)
|
| 184 |
+
sentence = starter + " " + sentence.lower()
|
| 185 |
|
| 186 |
+
modified_sentences.append(sentence)
|
| 187 |
|
| 188 |
+
return ' '.join(modified_sentences)
|
| 189 |
|
| 190 |
+
def add_natural_fillers(self, text):
|
| 191 |
+
"""Add natural filler words and hesitation"""
|
| 192 |
sentences = sent_tokenize(text)
|
| 193 |
+
modified_sentences = []
|
| 194 |
|
| 195 |
+
for sentence in sentences:
|
| 196 |
+
words = sentence.split()
|
| 197 |
+
|
| 198 |
+
# Add fillers occasionally
|
| 199 |
+
if len(words) > 6 and random.random() < 0.3:
|
| 200 |
+
filler = random.choice(self.human_fillers)
|
| 201 |
+
insert_position = random.randint(1, min(4, len(words) - 1))
|
| 202 |
+
words.insert(insert_position, filler)
|
|
|
|
|
|
|
| 203 |
|
| 204 |
+
modified_sentences.append(' '.join(words))
|
|
|
|
| 205 |
|
| 206 |
+
return ' '.join(modified_sentences)
|
| 207 |
|
| 208 |
+
def vary_sentence_structure(self, text):
|
| 209 |
+
"""Vary sentence structures for natural flow"""
|
| 210 |
+
sentences = sent_tokenize(text)
|
| 211 |
+
if len(sentences) < 2:
|
| 212 |
+
return text
|
| 213 |
+
|
| 214 |
+
modified_sentences = []
|
| 215 |
+
skip_next = False
|
| 216 |
|
| 217 |
+
for i, sentence in enumerate(sentences):
|
| 218 |
+
if skip_next:
|
| 219 |
+
skip_next = False
|
| 220 |
+
continue
|
| 221 |
+
|
| 222 |
+
# Combine short sentences occasionally
|
| 223 |
+
if (i < len(sentences) - 1 and
|
| 224 |
+
len(sentence.split()) < 8 and
|
| 225 |
+
len(sentences[i + 1].split()) < 8 and
|
| 226 |
+
random.random() < 0.4):
|
| 227 |
+
|
| 228 |
+
connectors = [' and ', ', ', ' - ', ' but ', ' so ']
|
| 229 |
+
connector = random.choice(connectors)
|
| 230 |
+
combined = sentence.rstrip('.!?') + connector + sentences[i + 1].lower()
|
| 231 |
+
modified_sentences.append(combined)
|
| 232 |
+
skip_next = True
|
| 233 |
+
else:
|
| 234 |
+
modified_sentences.append(sentence)
|
| 235 |
|
| 236 |
+
return ' '.join(modified_sentences)
|
| 237 |
|
| 238 |
+
def add_casual_punctuation(self, text):
|
| 239 |
+
"""Add more casual and varied punctuation"""
|
|
|
|
| 240 |
sentences = sent_tokenize(text)
|
| 241 |
+
modified_sentences = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
+
for i, sentence in enumerate(sentences):
|
| 244 |
+
# Sometimes use ellipsis for trailing thoughts
|
| 245 |
+
if random.random() < 0.1 and i == len(sentences) - 1:
|
| 246 |
+
sentence = sentence.rstrip('.!?') + '...'
|
| 247 |
+
|
| 248 |
+
# Sometimes use exclamation for emphasis
|
| 249 |
+
elif random.random() < 0.15:
|
| 250 |
+
emphasis_words = ['amazing', 'incredible', 'fantastic', 'great', 'awesome', 'really', 'very']
|
| 251 |
+
if any(word in sentence.lower() for word in emphasis_words):
|
| 252 |
+
sentence = sentence.rstrip('.') + '!'
|
| 253 |
+
|
| 254 |
+
modified_sentences.append(sentence)
|
| 255 |
|
| 256 |
+
return ' '.join(modified_sentences)
|
| 257 |
|
| 258 |
+
def clean_text(self, text):
|
| 259 |
+
"""Clean up formatting issues"""
|
| 260 |
+
# Fix multiple spaces
|
| 261 |
+
text = re.sub(r'\s+', ' ', text)
|
| 262 |
|
| 263 |
+
# Fix punctuation spacing
|
| 264 |
+
text = re.sub(r'\s+([.!?])', r'\1', text)
|
| 265 |
+
text = re.sub(r'([.!?])\s*([A-Z])', r'\1 \2', text)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
+
# Ensure proper capitalization after periods
|
| 268 |
+
def capitalize_after_period(match):
|
| 269 |
+
return match.group(1) + ' ' + match.group(2).upper()
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
+
text = re.sub(r'([.!?])\s+([a-z])', capitalize_after_period, text)
|
|
|
|
| 272 |
|
| 273 |
+
return text.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
def get_readability_score(self, text):
|
| 276 |
"""Calculate readability metrics"""
|
|
|
|
| 294 |
level = "Very Difficult"
|
| 295 |
|
| 296 |
return f"Flesch Score: {flesch_score:.1f} ({level})\nGrade Level: {fk_grade:.1f}"
|
| 297 |
+
except Exception as e:
|
| 298 |
+
return f"Could not calculate readability: {str(e)}"
|
| 299 |
+
|
| 300 |
+
def humanize_text(self, text, intensity="medium"):
|
| 301 |
+
"""Main humanization function"""
|
| 302 |
+
if not text or not text.strip():
|
| 303 |
+
return "Please provide text to humanize."
|
| 304 |
+
|
| 305 |
+
try:
|
| 306 |
+
# Clean input
|
| 307 |
+
text = text.strip()
|
| 308 |
+
|
| 309 |
+
# Apply humanization techniques based on intensity
|
| 310 |
+
text = self.replace_ai_phrases(text)
|
| 311 |
+
text = self.add_contractions(text)
|
| 312 |
+
|
| 313 |
+
if intensity in ["medium", "heavy"]:
|
| 314 |
+
text = self.vary_sentence_structure(text)
|
| 315 |
+
text = self.add_personal_touches(text)
|
| 316 |
+
text = self.add_casual_punctuation(text)
|
| 317 |
+
|
| 318 |
+
if intensity == "heavy":
|
| 319 |
+
text = self.add_natural_fillers(text)
|
| 320 |
+
|
| 321 |
+
# Final cleanup
|
| 322 |
+
text = self.clean_text(text)
|
| 323 |
+
|
| 324 |
+
return text
|
| 325 |
+
|
| 326 |
+
except Exception as e:
|
| 327 |
+
return f"Error processing text: {str(e)}\n\nOriginal text: {text}"
|
| 328 |
|
| 329 |
def create_interface():
|
| 330 |
humanizer = AIContentHumanizer()
|
| 331 |
|
| 332 |
def process_text(input_text, intensity):
|
| 333 |
if not input_text:
|
| 334 |
+
return "Please enter some text to humanize.", "No text provided for analysis."
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
+
try:
|
| 337 |
+
humanized = humanizer.humanize_text(input_text, intensity)
|
| 338 |
+
readability = humanizer.get_readability_score(humanized)
|
| 339 |
+
return humanized, readability
|
| 340 |
+
except Exception as e:
|
| 341 |
+
return f"Error: {str(e)}", "Error in processing"
|
| 342 |
|
| 343 |
# Custom CSS for better UI
|
| 344 |
css = """
|
| 345 |
.gradio-container {
|
| 346 |
font-family: 'Inter', sans-serif;
|
| 347 |
+
max-width: 1200px;
|
| 348 |
+
margin: 0 auto;
|
| 349 |
}
|
| 350 |
.main-header {
|
| 351 |
text-align: center;
|
| 352 |
margin-bottom: 30px;
|
| 353 |
+
padding: 20px;
|
| 354 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 355 |
+
border-radius: 15px;
|
| 356 |
+
color: white;
|
| 357 |
}
|
| 358 |
.feature-box {
|
| 359 |
border: 1px solid #e1e5e9;
|
| 360 |
+
border-radius: 12px;
|
| 361 |
+
padding: 20px;
|
| 362 |
+
margin: 15px 0;
|
| 363 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
| 364 |
+
}
|
| 365 |
+
.example-box {
|
| 366 |
+
background: #f8f9fa;
|
| 367 |
+
border-left: 4px solid #007bff;
|
| 368 |
padding: 15px;
|
| 369 |
margin: 10px 0;
|
| 370 |
+
border-radius: 5px;
|
| 371 |
}
|
| 372 |
"""
|
| 373 |
|
| 374 |
+
with gr.Blocks(css=css, title="AI Content Humanizer", theme=gr.themes.Soft()) as interface:
|
| 375 |
gr.HTML("""
|
| 376 |
<div class="main-header">
|
| 377 |
<h1>π€β‘οΈπ€ AI Content Humanizer</h1>
|
| 378 |
+
<p style="font-size: 18px; margin-top: 10px;">Transform AI-generated content into natural, human-like text</p>
|
| 379 |
+
<p style="font-size: 14px; opacity: 0.9;">Make your content sound more conversational and authentic</p>
|
| 380 |
</div>
|
| 381 |
""")
|
| 382 |
|
| 383 |
with gr.Row():
|
| 384 |
+
with gr.Column(scale=1):
|
| 385 |
input_text = gr.Textbox(
|
| 386 |
label="π Enter AI-generated text",
|
| 387 |
+
placeholder="Paste your AI-generated content here...\n\nExample: 'It is important to note that artificial intelligence has numerous applications in various industries. Furthermore, these technologies can significantly enhance productivity.'",
|
| 388 |
+
lines=12,
|
| 389 |
+
max_lines=25
|
| 390 |
)
|
| 391 |
|
| 392 |
+
with gr.Row():
|
| 393 |
+
intensity = gr.Radio(
|
| 394 |
+
choices=["light", "medium", "heavy"],
|
| 395 |
+
value="medium",
|
| 396 |
+
label="ποΈ Humanization Level",
|
| 397 |
+
info="Choose how much to humanize the text"
|
| 398 |
+
)
|
| 399 |
|
| 400 |
+
humanize_btn = gr.Button(
|
| 401 |
+
"β¨ Humanize Text",
|
| 402 |
+
variant="primary",
|
| 403 |
+
size="lg",
|
| 404 |
+
scale=1
|
| 405 |
+
)
|
| 406 |
|
| 407 |
+
with gr.Column(scale=1):
|
| 408 |
output_text = gr.Textbox(
|
| 409 |
label="β
Humanized Text",
|
| 410 |
+
lines=12,
|
| 411 |
+
max_lines=25,
|
| 412 |
+
interactive=True,
|
| 413 |
+
show_copy_button=True
|
| 414 |
)
|
| 415 |
|
| 416 |
readability_info = gr.Textbox(
|
|
|
|
| 419 |
interactive=False
|
| 420 |
)
|
| 421 |
|
| 422 |
+
# Information sections
|
| 423 |
+
with gr.Row():
|
| 424 |
+
with gr.Column():
|
| 425 |
+
gr.HTML("""
|
| 426 |
+
<div class="feature-box">
|
| 427 |
+
<h3>π― Humanization Features:</h3>
|
| 428 |
+
<ul style="text-align: left;">
|
| 429 |
+
<li><strong>π Smart Phrase Replacement:</strong> Replaces robotic AI phrases with natural expressions</li>
|
| 430 |
+
<li><strong>π¬ Conversational Tone:</strong> Adds contractions and casual language</li>
|
| 431 |
+
<li><strong>π Personal Touch:</strong> Incorporates opinions and personal perspectives</li>
|
| 432 |
+
<li><strong>π Natural Flow:</strong> Varies sentence structure and adds fillers</li>
|
| 433 |
+
<li><strong>π Readability Analysis:</strong> Provides reading level assessment</li>
|
| 434 |
+
</ul>
|
| 435 |
+
</div>
|
| 436 |
+
""")
|
| 437 |
+
|
| 438 |
+
with gr.Row():
|
| 439 |
+
with gr.Column():
|
| 440 |
+
gr.HTML("""
|
| 441 |
+
<div class="example-box">
|
| 442 |
+
<h4>π‘ Intensity Levels:</h4>
|
| 443 |
+
<p><strong>Light:</strong> Basic phrase replacement and contractions</p>
|
| 444 |
+
<p><strong>Medium:</strong> + Personal opinions and sentence restructuring</p>
|
| 445 |
+
<p><strong>Heavy:</strong> + Filler words and extensive casual modifications</p>
|
| 446 |
+
</div>
|
| 447 |
+
""")
|
| 448 |
|
| 449 |
# Example texts
|
| 450 |
examples = [
|
| 451 |
+
[
|
| 452 |
+
"It is important to note that artificial intelligence has numerous applications in various industries. Furthermore, machine learning algorithms can demonstrate significant improvements in efficiency. Therefore, organizations should utilize these technologies to facilitate better outcomes.",
|
| 453 |
+
"medium"
|
| 454 |
+
],
|
| 455 |
+
[
|
| 456 |
+
"In conclusion, the implementation of sustainable practices is crucial for environmental conservation. Moreover, it should be noted that organizations must demonstrate commitment to reducing their carbon footprint. Consequently, various strategies should be utilized to achieve these objectives.",
|
| 457 |
+
"heavy"
|
| 458 |
+
],
|
| 459 |
+
[
|
| 460 |
+
"The research demonstrates that renewable energy sources are becoming increasingly viable. However, it is important to note that substantial investment is required. Therefore, governments should implement policies that facilitate the adoption of clean energy technologies.",
|
| 461 |
+
"light"
|
| 462 |
+
]
|
| 463 |
]
|
| 464 |
|
| 465 |
gr.Examples(
|
| 466 |
examples=examples,
|
| 467 |
inputs=[input_text, intensity],
|
| 468 |
+
outputs=[output_text, readability_info],
|
| 469 |
+
fn=process_text,
|
| 470 |
+
cache_examples=True,
|
| 471 |
+
label="π Try these examples (click to load):"
|
| 472 |
)
|
| 473 |
|
| 474 |
+
# Event handlers
|
| 475 |
humanize_btn.click(
|
| 476 |
+
fn=process_text,
|
| 477 |
+
inputs=[input_text, intensity],
|
| 478 |
+
outputs=[output_text, readability_info],
|
| 479 |
+
show_progress=True
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
# Allow Enter key to trigger humanization
|
| 483 |
+
input_text.submit(
|
| 484 |
fn=process_text,
|
| 485 |
inputs=[input_text, intensity],
|
| 486 |
outputs=[output_text, readability_info]
|
|
|
|
| 489 |
return interface
|
| 490 |
|
| 491 |
if __name__ == "__main__":
|
| 492 |
+
print("Starting AI Content Humanizer...")
|
| 493 |
interface = create_interface()
|
| 494 |
+
interface.launch(
|
| 495 |
+
server_name="0.0.0.0",
|
| 496 |
+
server_port=7860,
|
| 497 |
+
show_error=True
|
| 498 |
+
)
|