Spaces:
Sleeping
Sleeping
| 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.") | |
| 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.") |