Humanizer-App / app.py
zay12121's picture
Update app.py
c1ca007 verified
import streamlit as st
from transformers import AutoTokenizer, T5ForConditionalGeneration
import torch
# --- Streamlit Page Configuration ---
st.set_page_config(layout="wide", page_title="Free Text Humanizer")
# --- Custom CSS for Enhanced Aesthetics ---
# This CSS block styles the Streamlit application to give it a modern,
# user-friendly look with the 'Inter' font, rounded corners, and a
# consistent pink/purple color scheme.
st.markdown(
"""
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap');
/* Apply Inter font and dark text color to the entire app */
html, body, [class*="st-emotion"] {
font-family: 'Inter', sans-serif;
color: #333333; /* Darker gray for general text for better contrast */
}
/* Main app background gradient */
.stApp {
background: linear-gradient(to right, #ffebee, #e1bee7); /* Light pink to light purple */
}
/* Main content area styling (card-like effect) */
/* Targets the primary container where Streamlit widgets are rendered */
.st-emotion-cache-1cypcdb {
padding: 2rem;
border-radius: 1rem;
background-color: rgba(255, 255, 255, 0.9); /* Slightly transparent white background */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
/* Sidebar styling (if you were to add one later) */
.st-emotion-cache-1dp5vir {
background: linear-gradient(to bottom, #d8bfd8, #e6e6fa); /* Softer gradient for sidebar */
border-radius: 1.5rem; /* Slightly more rounded */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
padding: 1.5rem;
}
.st-emotion-cache-1dp5vir .st-emotion-cache-vk330y { /* Sidebar header text */
color: #4a004a; /* Darker purple for text */
}
/* Text Area styling */
.stTextArea textarea {
border-radius: 0.75rem;
border: 1px solid #e0b0ff; /* Light purple border */
padding: 1rem;
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.05);
color: #333333; /* Ensure text typed in textarea is dark */
}
/* Button styling */
.stButton > button {
background-color: #8e24aa;
color: white;
border-radius: 0.75rem;
padding: 0.75rem 1.5rem;
font-weight: 600;
transition: all 0.2s ease-in-out;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.06);
}
.stButton > button:hover {
background-color: #6a1b9a;
transform: translateY(-2px);
box-shadow: 0 6px 10px -2px rgba(0, 0, 0, 0.15), 0 4px 6px -3px rgba(0, 0, 0, 0.08);
}
/* Selectbox styling (if you were to add one later) */
.stSelectbox > div > div {
border-radius: 0.75rem;
border: 1px solid #e0b0ff;
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.05);
color: #333333; /* Ensure selectbox text is dark */
}
.stSelectbox > div > div > div > div { /* Target selected value text */
color: #333333;
}
/* Metric styling (if you were to add one later) */
.stMetric {
background-color: #f3e5f5;
border-radius: 0.75rem;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.05), 0 1px 2px -1px rgba(0, 0, 0, 0.03);
}
.stMetric label {
color: #4a004a;
font-weight: 600;
}
.stMetric .css-10trblm {
color: #8e24aa;
font-size: 2.25rem;
font-weight: 700;
}
/* Alert styling (Error, Warning, Success, Info) */
.stAlert {
border-radius: 0.75rem;
padding: 1rem 1.5rem;
margin-top: 1rem;
font-weight: 500;
}
/* Specific alert colors, ensuring they override defaults */
.stAlert.st-emotion-cache-1f062a7 { /* Error alert */
background-color: #ffebee !important;
border-left: 5px solid #d32f2f !important;
color: #d32f2f !important;
}
.stAlert.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7 { /* Warning alert */
background-color: #fff8e1 !important;
border-left: 5px solid #ffa000 !important;
color: #ffa000 !important;
}
.stAlert.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7 { /* Success alert */
background-color: #e8f5e9 !important;
border-left: 5px solid #388e3c !important;
color: #388e3c !important;
}
.stAlert.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7 { /* Info alert */
background-color: #e3f2fd !important;
border-left: 5px solid #1976d2 !important;
color: #1976d2 !important;
}
/* Header styling */
h1, h2, h3, h4, h5, h6 {
color: #4a004a; /* Darker purple for headers */
font-weight: 700; /* Ensure headers are bold */
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
h2 {
font-size: 2rem;
margin-top: 1.5rem;
margin-bottom: 1rem;
}
h3 {
font-size: 1.5rem;
margin-top: 1.25rem;
margin-bottom: 0.75rem;
}
/* Markdown text styling */
.st-emotion-cache-10trblm p {
line-height: 1.7; /* Increased line height for better readability */
font-size: 1.05rem; /* Slightly larger font size for body text */
color: #333333; /* Ensure general markdown text is dark */
}
.st-emotion-cache-10trblm li { /* List items */
line-height: 1.6;
font-size: 1.05rem;
color: #333333;
}
/* Debugging info text (specifically targeting the info alert style) */
.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7.st-emotion-cache-1f062a7 {
color: #1976d2 !important; /* Ensure info text is clear */
}
/* Progress bar styling */
.st-emotion-cache-1r6y40 {
border-radius: 0.5rem;
overflow: hidden;
margin-top: 0.5rem;
margin-bottom: 1rem;
height: 1.5rem; /* Make it a bit taller */
background-color: #e0e0e0;
}
.st-emotion-cache-1r6y40 > div > div {
background-color: #8e24aa;
border-radius: 0.5rem;
}
</style>
""",
unsafe_allow_html=True
)
st.title("Free Text Humanizer (Public Models)")
st.markdown("Rewrite text for a more natural, human-like tone using publicly available models.")
@st.cache_resource
def load_model_and_tokenizer(model_name):
"""
Loads a T5 model and tokenizer from Hugging Face for humanization.
Ensures no authentication is needed.
Moves the model to the appropriate device (GPU/CPU) immediately.
"""
try:
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = T5ForConditionalGeneration.from_pretrained(model_name)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
st.success(f"Successfully loaded model: {model_name} on {device}")
return tokenizer, model, device
except Exception as e:
st.error(f"Error loading model '{model_name}'. Please check the model name, ensure it's public, and verify your internet connection. Error: {e}")
return None, None, None
# --- Humanizer Model Configuration ---
HUMANIZER_MODEL_NAME = "t5-base" # Using the T5-base model for paraphrasing/humanization
HUMANIZER_MODEL_LABEL = "T5-base (for Humanizer)"
# --- Load Humanizer Model ---
HUMANIZER_MODEL_LOADED = {}
with st.spinner(f"Loading humanizer model ({HUMANIZER_MODEL_LABEL})... This may take a moment."):
tokenizer_humanizer, model_humanizer, device_humanizer = load_model_and_tokenizer(HUMANIZER_MODEL_NAME)
if tokenizer_humanizer and model_humanizer:
HUMANIZER_MODEL_LOADED[HUMANIZER_MODEL_LABEL] = (tokenizer_humanizer, model_humanizer, device_humanizer)
# --- Check if Humanizer Model Loaded Successfully ---
if not HUMANIZER_MODEL_LOADED:
st.error("Humanizer model could not be loaded. Please check your internet connection or the model name.")
st.stop() # Stop the app if the essential model isn't available
# --- Text Input for Humanizer ---
st.header("1. Text Humanizer")
humanizer_text = st.text_area(
"Paste text here to make it sound more natural:",
height=300,
placeholder="Type or paste text here to rewrite it with a more human-like tone.",
key="humanizer_text_area" # Unique key for this Streamlit widget
)
# --- Debugging Information for Input Text ---
st.info(f"Humanizer input length: {len(humanizer_text)} characters.")
if humanizer_text.strip():
st.info(f"First 50 characters of humanizer input: '{humanizer_text.strip()[:50]}...'")
# --- Humanize Button Logic ---
if st.button("Humanize Text", key="humanize_button"):
st.write("Humanize button clicked! Starting rewriting process...") # Debugging feedback
if not humanizer_text.strip():
st.warning("Please enter some text to humanize.")
else:
st.subheader("Humanized Text:")
# Retrieve the loaded model components
tokenizer_h, model_h, device_h = HUMANIZER_MODEL_LOADED[HUMANIZER_MODEL_LABEL]
with st.spinner("Rewriting text..."):
try:
# T5 models often require a specific prefix for tasks like paraphrasing
input_text_for_t5 = "paraphrase: " + humanizer_text
st.info(f"Input to T5 model: '{input_text_for_t5[:100]}...'") # Debugging: show T5 input
# Tokenize the input text and move it to the correct device (CPU/GPU)
inputs = tokenizer_h(input_text_for_t5, return_tensors="pt", max_length=512, truncation=True)
inputs = {k: v.to(device_h) for k, v in inputs.items()}
st.info(f"Tokenized input length: {inputs['input_ids'].shape[1]}") # Debugging: token length
# Generate the rewritten text using the T5 model
# Increased max_length to allow for more substantial changes
# num_beams: controls the number of beams for beam search (higher = better quality, slower)
# early_stopping: stops generation when all beam hypotheses have finished
outputs = model_h.generate(
inputs["input_ids"],
attention_mask=inputs["attention_mask"],
max_length=256, # Adjusted max_length for potentially more varied output
num_beams=8, # Increased num_beams for higher quality paraphrasing
early_stopping=True,
no_repeat_ngram_size=2 # Added to reduce repetitive phrases
)
# Decode the generated token IDs back into human-readable text
humanized_output = tokenizer_h.decode(outputs[0], skip_special_tokens=True)
st.info(f"Generated output length: {len(humanized_output)} characters.") # Debugging: output length
st.markdown("---")
st.markdown("#### Original Text:")
st.write(humanizer_text) # Display the original text
st.markdown("#### Rewritten (Humanized) Text:")
st.success(humanized_output) # Display the humanized text with a success style
# --- New Debugging/Feedback for User ---
if humanized_output.strip() == humanizer_text.strip():
st.warning("⚠️ The humanizer produced output identical to the input. This can happen with very short or simple texts, or if the model finds no significant changes to make.")
elif len(humanized_output.strip()) < len(humanizer_text.strip()) * 0.5: # Arbitrary check for very short output
st.warning("⚠️ The humanized output is significantly shorter than the original. The model might have truncated or simplified the text too much.")
st.markdown(
"""
**Disclaimer:** This "Humanizer" uses a paraphrasing model to rewrite text for a more natural tone.
It **does not guarantee** that the output will bypass AI detection tools.
The effectiveness varies based on the input text and the complexity of the desired "human" style.
"""
)
except Exception as e:
st.error(f"Failed to humanize text: {e}")
st.exception(e) # Display full traceback for detailed debugging
st.markdown("---")
st.markdown("### How the Humanizer Works:")
st.markdown(
"""
The "Text Humanizer" uses a **T5-base** model, a powerful text-to-text transformer. When you provide text, the model paraphrases it, aiming to:
* Vary sentence structures and vocabulary.
* Introduce more natural phrasing.
* Potentially make the text less predictable to language models.
**Important Note:** This tool is designed to *rewrite* text for improved naturalness. It **does not guarantee** that the output will bypass AI detection. The effectiveness of "humanization" is subjective and depends on the original text's characteristics and the specific AI detector being used.
"""
)
st.markdown("---")
st.markdown("Made with ❤️ using Streamlit and Hugging Face Transformers.")