Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,16 +1,20 @@
|
|
|
|
1 |
import gradio as gr
|
2 |
import random
|
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',
|
12 |
-
nltk.download('averaged_perceptron_tagger',
|
13 |
-
nltk.download('stopwords',
|
|
|
14 |
print("NLTK data downloaded successfully")
|
15 |
except Exception as e:
|
16 |
print(f"NLTK download error: {e}")
|
@@ -18,89 +22,30 @@ except Exception as e:
|
|
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 |
-
|
29 |
-
|
30 |
-
],
|
31 |
-
r'\
|
32 |
-
|
33 |
-
|
34 |
-
],
|
35 |
-
r'\
|
36 |
-
|
37 |
-
|
38 |
-
],
|
39 |
-
r'\
|
40 |
-
|
41 |
-
|
42 |
-
],
|
43 |
-
r'\
|
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",
|
@@ -123,376 +68,132 @@ class AIContentHumanizer:
|
|
123 |
r'\bwas not\b': "wasn't",
|
124 |
r'\bwere not\b': "weren't"
|
125 |
}
|
126 |
-
|
127 |
-
|
128 |
-
self.
|
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)):
|
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 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
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 |
-
|
194 |
-
|
195 |
-
|
196 |
-
words = sentence.split()
|
197 |
-
|
198 |
-
# Add fillers occasionally
|
199 |
if len(words) > 6 and random.random() < 0.3:
|
200 |
-
|
201 |
-
|
202 |
-
|
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 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
skip_next = False
|
216 |
-
|
217 |
-
for i, sentence in enumerate(sentences):
|
218 |
-
if skip_next:
|
219 |
-
skip_next = False
|
220 |
continue
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
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 |
-
|
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"""
|
277 |
try:
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
level = "Very Easy"
|
283 |
-
elif flesch_score >= 80:
|
284 |
-
level = "Easy"
|
285 |
-
elif flesch_score >= 70:
|
286 |
-
level = "Fairly Easy"
|
287 |
-
elif flesch_score >= 60:
|
288 |
-
level = "Standard"
|
289 |
-
elif flesch_score >= 50:
|
290 |
-
level = "Fairly Difficult"
|
291 |
-
elif flesch_score >= 30:
|
292 |
-
level = "Difficult"
|
293 |
-
else:
|
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
|
335 |
-
|
336 |
try:
|
337 |
-
|
338 |
-
|
339 |
-
return
|
340 |
except Exception as e:
|
341 |
-
return f"Error: {str(e)}", "
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
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(
|
417 |
-
label="📊 Readability Analysis",
|
418 |
-
lines=3,
|
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]
|
487 |
-
)
|
488 |
-
|
489 |
return interface
|
490 |
|
491 |
if __name__ == "__main__":
|
492 |
print("Starting AI Content Humanizer...")
|
493 |
-
|
494 |
-
|
495 |
-
server_name="0.0.0.0",
|
496 |
-
server_port=7860,
|
497 |
-
show_error=True
|
498 |
-
)
|
|
|
1 |
+
import os
|
2 |
import gradio as gr
|
3 |
import random
|
4 |
import re
|
5 |
import nltk
|
6 |
from nltk.tokenize import sent_tokenize, word_tokenize
|
|
|
7 |
from textstat import flesch_reading_ease, flesch_kincaid_grade
|
8 |
|
9 |
+
# Setup NLTK download path
|
10 |
+
os.environ['NLTK_DATA'] = '/tmp/nltk_data'
|
11 |
+
|
12 |
# Download required NLTK data
|
13 |
try:
|
14 |
+
nltk.download('punkt', download_dir='/tmp/nltk_data')
|
15 |
+
nltk.download('averaged_perceptron_tagger', download_dir='/tmp/nltk_data')
|
16 |
+
nltk.download('stopwords', download_dir='/tmp/nltk_data')
|
17 |
+
nltk.data.path.append('/tmp/nltk_data')
|
18 |
print("NLTK data downloaded successfully")
|
19 |
except Exception as e:
|
20 |
print(f"NLTK download error: {e}")
|
|
|
22 |
class AIContentHumanizer:
|
23 |
def __init__(self):
|
24 |
self.setup_humanization_patterns()
|
25 |
+
|
26 |
def setup_humanization_patterns(self):
|
|
|
|
|
|
|
27 |
self.ai_replacements = {
|
28 |
+
r'\bit is important to note that\b': ["worth mentioning that", "keep in mind that", "note that"],
|
29 |
+
r'\bit should be noted that\b': ["remember that", "worth noting that", "keep in mind"],
|
30 |
+
r'\bin conclusion\b': ["to wrap up", "all in all", "bottom line"],
|
31 |
+
r'\bto conclude\b': ["to wrap up", "all in all", "in the end"],
|
32 |
+
r'\bfurthermore\b': ["also", "plus", "what's more"],
|
33 |
+
r'\bmoreover\b': ["also", "plus", "and"],
|
34 |
+
r'\bhowever\b': ["but", "though", "yet"],
|
35 |
+
r'\btherefore\b': ["so", "that's why", "which means"],
|
36 |
+
r'\bconsequently\b': ["so", "as a result", "that's why"],
|
37 |
+
r'\bsignificant(?:ly)?\b': ["big", "major", "important"],
|
38 |
+
r'\bnumerous\b': ["many", "lots of", "plenty of"],
|
39 |
+
r'\butilize\b': ["use", "make use of", "work with"],
|
40 |
+
r'\bdemonstrate\b': ["show", "prove", "make clear"],
|
41 |
+
r'\bfacilitate\b': ["help", "make easier", "enable"],
|
42 |
+
r'\bimplement\b': ["put in place", "set up", "start using"],
|
43 |
+
r'\bvarious\b': ["different", "several", "many"],
|
44 |
+
r'\bsubstantial\b': ["big", "major", "significant"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
}
|
|
|
|
|
46 |
self.contractions = {
|
47 |
r'\bit is\b': "it's",
|
48 |
r'\bthat is\b': "that's",
|
|
|
|
|
|
|
49 |
r'\bwe are\b': "we're",
|
50 |
r'\bthey are\b': "they're",
|
51 |
r'\byou are\b': "you're",
|
|
|
68 |
r'\bwas not\b': "wasn't",
|
69 |
r'\bwere not\b': "weren't"
|
70 |
}
|
71 |
+
self.human_fillers = ['actually', 'basically', 'really', 'pretty much']
|
72 |
+
self.opinion_markers = ["I think", "I believe", "In my opinion"]
|
73 |
+
self.casual_starters = ["Look,", "Listen,", "Here's the thing:"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
def replace_ai_phrases(self, text):
|
|
|
76 |
for pattern, replacements in self.ai_replacements.items():
|
77 |
matches = re.finditer(pattern, text, re.IGNORECASE)
|
78 |
+
for match in reversed(list(matches)):
|
79 |
replacement = random.choice(replacements)
|
80 |
start, end = match.span()
|
|
|
81 |
if text[start].isupper():
|
82 |
replacement = replacement.capitalize()
|
83 |
text = text[:start] + replacement + text[end:]
|
84 |
return text
|
85 |
|
86 |
def add_contractions(self, text):
|
|
|
87 |
for pattern, contraction in self.contractions.items():
|
88 |
text = re.sub(pattern, contraction, text, flags=re.IGNORECASE)
|
89 |
return text
|
90 |
|
91 |
def add_personal_touches(self, text):
|
|
|
92 |
sentences = sent_tokenize(text)
|
93 |
+
modified = []
|
94 |
+
for i, s in enumerate(sentences):
|
95 |
+
if random.random() < 0.3:
|
96 |
+
s = random.choice(self.opinion_markers) + " " + s.lower()
|
97 |
+
elif i == 0 and random.random() < 0.2:
|
98 |
+
s = random.choice(self.casual_starters) + " " + s.lower()
|
99 |
+
modified.append(s)
|
100 |
+
return ' '.join(modified)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
def add_natural_fillers(self, text):
|
|
|
103 |
sentences = sent_tokenize(text)
|
104 |
+
modified = []
|
105 |
+
for s in sentences:
|
106 |
+
words = s.split()
|
|
|
|
|
|
|
107 |
if len(words) > 6 and random.random() < 0.3:
|
108 |
+
words.insert(random.randint(1, min(4, len(words)-1)), random.choice(self.human_fillers))
|
109 |
+
modified.append(' '.join(words))
|
110 |
+
return ' '.join(modified)
|
|
|
|
|
|
|
|
|
111 |
|
112 |
def vary_sentence_structure(self, text):
|
|
|
113 |
sentences = sent_tokenize(text)
|
114 |
+
modified, skip = [], False
|
115 |
+
for i in range(len(sentences)):
|
116 |
+
if skip:
|
117 |
+
skip = False
|
|
|
|
|
|
|
|
|
|
|
118 |
continue
|
119 |
+
if i < len(sentences)-1 and len(sentences[i].split()) < 8 and len(sentences[i+1].split()) < 8 and random.random() < 0.4:
|
120 |
+
combined = sentences[i].rstrip('.!?') + ', ' + sentences[i+1].lower()
|
121 |
+
modified.append(combined)
|
122 |
+
skip = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
else:
|
124 |
+
modified.append(sentences[i])
|
125 |
+
return ' '.join(modified)
|
|
|
126 |
|
127 |
def add_casual_punctuation(self, text):
|
|
|
128 |
sentences = sent_tokenize(text)
|
129 |
+
modified = []
|
130 |
+
for i, s in enumerate(sentences):
|
|
|
|
|
131 |
if random.random() < 0.1 and i == len(sentences) - 1:
|
132 |
+
s = s.rstrip('.!?') + '...'
|
133 |
+
elif random.random() < 0.15 and any(word in s.lower() for word in ['amazing', 'incredible']):
|
134 |
+
s = s.rstrip('.') + '!'
|
135 |
+
modified.append(s)
|
136 |
+
return ' '.join(modified)
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
|
138 |
def clean_text(self, text):
|
|
|
|
|
139 |
text = re.sub(r'\s+', ' ', text)
|
|
|
|
|
140 |
text = re.sub(r'\s+([.!?])', r'\1', text)
|
141 |
text = re.sub(r'([.!?])\s*([A-Z])', r'\1 \2', text)
|
142 |
+
def cap(match): return match.group(1) + ' ' + match.group(2).upper()
|
143 |
+
text = re.sub(r'([.!?])\s+([a-z])', cap, text)
|
|
|
|
|
|
|
|
|
|
|
144 |
return text.strip()
|
145 |
|
146 |
def get_readability_score(self, text):
|
|
|
147 |
try:
|
148 |
+
score = flesch_reading_ease(text)
|
149 |
+
grade = flesch_kincaid_grade(text)
|
150 |
+
level = ("Very Easy" if score >= 90 else "Easy" if score >= 80 else "Fairly Easy" if score >= 70 else "Standard" if score >= 60 else "Fairly Difficult" if score >= 50 else "Difficult" if score >= 30 else "Very Difficult")
|
151 |
+
return f"Flesch Score: {score:.1f} ({level})\nGrade Level: {grade:.1f}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
except Exception as e:
|
153 |
return f"Could not calculate readability: {str(e)}"
|
154 |
|
155 |
def humanize_text(self, text, intensity="medium"):
|
|
|
156 |
if not text or not text.strip():
|
157 |
return "Please provide text to humanize."
|
|
|
158 |
try:
|
|
|
159 |
text = text.strip()
|
|
|
|
|
160 |
text = self.replace_ai_phrases(text)
|
161 |
text = self.add_contractions(text)
|
|
|
162 |
if intensity in ["medium", "heavy"]:
|
163 |
text = self.vary_sentence_structure(text)
|
164 |
text = self.add_personal_touches(text)
|
165 |
text = self.add_casual_punctuation(text)
|
|
|
166 |
if intensity == "heavy":
|
167 |
text = self.add_natural_fillers(text)
|
168 |
+
return self.clean_text(text)
|
|
|
|
|
|
|
|
|
|
|
169 |
except Exception as e:
|
170 |
return f"Error processing text: {str(e)}\n\nOriginal text: {text}"
|
171 |
|
172 |
def create_interface():
|
173 |
humanizer = AIContentHumanizer()
|
|
|
174 |
def process_text(input_text, intensity):
|
175 |
if not input_text:
|
176 |
+
return "Please enter some text to humanize.", "No text provided."
|
|
|
177 |
try:
|
178 |
+
result = humanizer.humanize_text(input_text, intensity)
|
179 |
+
score = humanizer.get_readability_score(result)
|
180 |
+
return result, score
|
181 |
except Exception as e:
|
182 |
+
return f"Error: {str(e)}", "Processing error"
|
183 |
+
|
184 |
+
with gr.Blocks(title="AI Content Humanizer") as interface:
|
185 |
+
gr.Markdown("""# 🤖➡️👤 AI Content Humanizer
|
186 |
+
Transform AI-generated content into human-sounding, casual, and readable text!""")
|
187 |
+
input_text = gr.Textbox(label="AI-generated Text", lines=8)
|
188 |
+
intensity = gr.Radio(["light", "medium", "heavy"], value="medium", label="Humanization Level")
|
189 |
+
output_text = gr.Textbox(label="Humanized Text", lines=8, show_copy_button=True)
|
190 |
+
readability = gr.Textbox(label="Readability Score", lines=2)
|
191 |
+
btn = gr.Button("Humanize Text")
|
192 |
+
btn.click(fn=process_text, inputs=[input_text, intensity], outputs=[output_text, readability])
|
193 |
+
input_text.submit(fn=process_text, inputs=[input_text, intensity], outputs=[output_text, readability])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
return interface
|
195 |
|
196 |
if __name__ == "__main__":
|
197 |
print("Starting AI Content Humanizer...")
|
198 |
+
app = create_interface()
|
199 |
+
app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
|
|
|
|
|
|
|
|