Spaces:
Running
Running
import gradio as gr | |
import requests | |
from bs4 import BeautifulSoup | |
from nltk import download, sent_tokenize | |
import google.generativeai as genai | |
import os | |
import re | |
import tempfile | |
import asyncio | |
import time | |
# Download NLTK data | |
download('punkt') | |
download('punkt_tab') | |
# Configure Gemini API using environment variable | |
api_key = os.environ.get("GEMINI_API_KEY") | |
if not api_key: | |
raise ValueError("GEMINI_API_KEY not found in environment variables. Please set it in your environment.") | |
genai.configure(api_key=api_key) | |
# Use gemini-1.5-flash for faster and more accessible text analysis | |
try: | |
model = genai.GenerativeModel('gemini-1.5-flash') | |
except Exception as e: | |
print(f"Error initializing model: {str(e)}") | |
print("Available models:") | |
for m in genai.list_models(): | |
print(m.name) | |
raise ValueError("Failed to initialize gemini-1.5-flash. Check available models above and update the model name.") | |
# Prompt for Gemini to analyze text with specified output format | |
PROMPT = """ | |
You are an AI content reviewer. Analyze the provided text for the following: | |
1. Grammar Issues: Identify and suggest corrections for grammatical errors. | |
2. Legal Policy Violations: Flag content that may violate common legal policies (e.g., copyright infringement, defamation, incitement to violence). | |
3. Crude/Abusive Language: Detect crude, offensive, or abusive language. | |
4. Sensitive Topics: Identify content related to sensitive topics such as racism, gender bias, or other forms of discrimination. | |
Return the results in the following markdown format: | |
# Blog Review Report | |
## Grammar Corrections | |
1. [Heading of issue] | |
- CONTENT: [Exact line or part of text with the issue] | |
- SUGGESTION: [Suggested correction] | |
- ISSUE: [Description of the issue] | |
2. [Heading of next issue] | |
- CONTENT: [Exact line or part of text with the issue] | |
- SUGGESTION: [Suggested correction] | |
- ISSUE: [Description of the issue] | |
[Continue numbering for additional issues or state "None detected"] | |
## Legal Policy Violations | |
- CONTENT: [Exact line or part of text with the issue] | |
SUGGESTION: [Suggested action or correction] | |
ISSUE: [Description of the legal violation] | |
[Or state "None detected"] | |
## Crude/Abusive Language | |
- [List instances of crude or abusive language or "None detected"] | |
## Sensitive Topics | |
- [List instances of sensitive topics or "None detected"] | |
For each issue, provide the exact text, a suggested correction or action, and a concise explanation. Be precise and ensure the output strictly follows the specified format. | |
""" | |
async def fetch_url_content(url): | |
try: | |
response = requests.get(url, timeout=10) | |
response.raise_for_status() | |
soup = BeautifulSoup(response.text, 'html.parser') | |
content = ' '.join([p.get_text(strip=True) for p in soup.find_all(['p', 'article', 'div'])]) | |
return content if content else "No readable content found on the page." | |
except Exception as e: | |
return f"Error fetching URL: {str(e)}" | |
async def review_blog(text_input, url_input, progress=gr.Progress()): | |
if text_input and not url_input: | |
input_type = "Text" | |
input_text = text_input | |
elif url_input and not text_input: | |
input_type = "URL" | |
input_text = url_input | |
else: | |
return "Review Blog", "Error: Please provide input in either the Text or URL tab, but not both.", gr.update(visible=False) | |
if not input_text: | |
return "Review Blog", "Error: No input provided.", gr.update(visible=False) | |
if input_type == "URL": | |
progress(0, desc="Fetching content...") | |
input_text = await fetch_url_content(input_text) | |
if input_text.startswith("Error"): | |
return "Review Blog", input_text, gr.update(visible=False) | |
sentences = sent_tokenize(input_text) | |
analysis_text = "\n".join(sentences) | |
progress(0, desc="Generating report...") | |
start_time = time.time() | |
for i in range(1, 10): | |
await asyncio.sleep(1) | |
progress(i / 10, desc=f"Generating report... ({int(i * 10)}%)") | |
if time.time() - start_time > 30: | |
return "Review Blog", "Error: API request timed out after 30 seconds.", gr.update(visible=False) | |
try: | |
response = await asyncio.to_thread(model.generate_content, PROMPT + "\n\nText to analyze:\n" + analysis_text) | |
report = response.text.strip() | |
report = re.sub(r'^markdown\n|$', '', report, flags=re.MULTILINE) | |
except Exception as e: | |
report = f"Error analyzing content with Gemini: {str(e)}" | |
print("Available models:") | |
for m in genai.list_models(): | |
print(m.name) | |
return "Review Blog", report, gr.update(visible=False) | |
try: | |
with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as temp_file: | |
temp_file.write(report) | |
temp_file_path = temp_file.name | |
progress(1.0, desc="Report generated!") | |
return "Review Blog", report, gr.update(visible=True, value=temp_file_path) | |
except Exception as e: | |
return "Review Blog", f"Error creating temporary file: {str(e)}", gr.update(visible=False) | |
# Enhanced custom CSS for a more attractive and user-friendly UI | |
custom_css = """ | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); | |
.gradio-container { | |
font-family: 'Inter', sans-serif !important; | |
background: linear-gradient(135deg, #f0f4f8 0%, #d9e2ec 100%); | |
padding: 30px; | |
border-radius: 15px; | |
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.15); | |
color: #2d3748; | |
} | |
h1, h3 { | |
color: #2b6cb0; | |
text-align: center; | |
font-weight: 700; | |
margin-bottom: 15px; | |
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1); | |
} | |
.review-btn { | |
background: linear-gradient(45deg, #4a90e2, #63b3ed); | |
color: white; | |
font-weight: 600; | |
border: none; | |
border-radius: 12px; | |
padding: 14px 28px; | |
transition: all 0.3s ease; | |
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3); | |
cursor: pointer; | |
} | |
.review-btn:hover { | |
background: linear-gradient(45deg, #63b3ed, #4a90e2); | |
transform: translateY(-3px); | |
box-shadow: 0 6px 15px rgba(74, 144, 226, 0.4); | |
} | |
.review-btn:disabled { | |
opacity: 0.7; | |
cursor: not-allowed; | |
transform: none; | |
box-shadow: none; | |
} | |
.review-btn:disabled::after { | |
content: ' β³'; | |
} | |
.tab-nav button { | |
font-family: 'Inter', sans-serif; | |
font-weight: 600; | |
background: #edf2f7; | |
color: #2d3748; | |
border-radius: 10px 10px 0 0; | |
padding: 12px 24px; | |
transition: all 0.2s ease; | |
border: 1px solid #e2e8f0; | |
} | |
.tab-nav button:hover { | |
background: #4a90e2; | |
color: white; | |
border-color: #4a90e2; | |
} | |
input, textarea { | |
font-family: 'Inter', sans-serif; | |
border: 2px solid #e2e8f0; | |
border-radius: 10px; | |
padding: 12px; | |
background: #ffffff; | |
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); | |
color: #2d3748; | |
font-size: 16px; | |
} | |
.gr-textbox textarea { | |
resize: vertical; | |
min-height: 150px; | |
} | |
.gr-progress { | |
background: #edf2f7; | |
border-radius: 12px; | |
overflow: hidden; | |
height: 30px; | |
} | |
.gr-progress > div { | |
background: linear-gradient(90deg, #48bb78, #38a169); | |
height: 100%; | |
transition: width 0.5s ease; | |
} | |
.markdown-container { | |
background: #ffffff; | |
border-radius: 12px; | |
padding: 25px; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); | |
margin-top: 20px; | |
color: #2d3748; | |
} | |
.download-btn { | |
background: #48bb78; | |
color: white; | |
border-radius: 10px; | |
padding: 12px 24px; | |
font-weight: 600; | |
transition: all 0.3s ease; | |
cursor: pointer; | |
} | |
.download-btn:hover { | |
background: #38a169; | |
transform: translateY(-3px); | |
} | |
""" | |
# Gradio UI with enhanced layout | |
with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css) as demo: | |
gr.Markdown( | |
""" | |
# π AI Blog Reviewer | |
Easily analyze your blog for grammar, legal issues, crude language, and sensitive topics. | |
""" | |
) | |
with gr.Row(): | |
with gr.Column(scale=2): | |
with gr.Tabs(): | |
with gr.TabItem("Text Input"): | |
text_input = gr.Textbox( | |
lines=10, | |
label="Paste Your Blog Content", | |
placeholder="Enter your blog text here...", | |
elem_classes=["input-text"] | |
) | |
with gr.TabItem("URL Input"): | |
url_input = gr.Textbox( | |
lines=1, | |
label="Enter Blog URL", | |
placeholder="https://example.com/blog", | |
elem_classes=["input-url"] | |
) | |
with gr.Column(scale=1): | |
gr.Markdown( | |
""" | |
### How It Works | |
1. **Input**: Paste text or provide a URL. | |
2. **Analysis**: AI checks grammar, legal issues, and more. | |
3. **Report**: Download a detailed markdown report. | |
""" | |
) | |
status_button = gr.Button("Review Blog", elem_classes=["review-btn"]) | |
gr.Markdown("### π Review Report") | |
with gr.Row(): | |
with gr.Column(): | |
report_output = gr.Markdown(elem_classes=["markdown-container"]) | |
download_btn = gr.File(label="Download Report", visible=False, elem_classes=["download-btn"]) | |
status_button.click( | |
fn=review_blog, | |
inputs=[text_input, url_input], | |
outputs=[status_button, report_output, download_btn] | |
) | |
demo.launch() |