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 tokenizer data | |
download('punkt') | |
# Set Gemini API key | |
api_key = os.environ.get("GEMINI_API_KEY") | |
if not api_key: | |
raise ValueError("GEMINI_API_KEY not found in environment variables.") | |
genai.configure(api_key=api_key) | |
try: | |
model = genai.GenerativeModel('gemini-1.5-flash') | |
except Exception as e: | |
print(f"Error initializing model: {e}") | |
print("Available models:") | |
for m in genai.list_models(): | |
print(m.name) | |
raise | |
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] | |
[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"] | |
""" | |
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 or "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_text = text_input | |
elif url_input and not text_input: | |
input_text = await fetch_url_content(url_input) | |
if input_text.startswith("Error"): | |
return "Review Blog", input_text, gr.update(visible=False) | |
else: | |
return "Review Blog", "Error: Provide either text or URL, not both.", gr.update(visible=False) | |
sentences = sent_tokenize(input_text) | |
analysis_text = "\n".join(sentences) | |
progress(0.2, desc="Analyzing...") | |
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: | |
return "Review Blog", f"Error with Gemini API: {str(e)}", gr.update(visible=False) | |
try: | |
with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as temp_file: | |
temp_file.write(report) | |
file_path = temp_file.name | |
progress(1.0, desc="Report Ready!") | |
return "Review Blog", report, gr.update(visible=True, value=file_path) | |
except Exception as e: | |
return "Review Blog", f"Error saving report: {str(e)}", gr.update(visible=False) | |
# ------------------ CSS ------------------ # | |
custom_css = """ | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap'); | |
.gradio-container { | |
font-family: 'Inter', sans-serif !important; | |
background: linear-gradient(120deg, #e0f7fa 0%, #f9f9f9 100%); | |
padding: 40px; | |
border-radius: 20px; | |
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.07); | |
} | |
h1, h3 { | |
font-weight: 700; | |
color: #2b6cb0; | |
} | |
input, textarea { | |
border: 2px solid #cbd5e0; | |
border-radius: 12px; | |
padding: 15px; | |
background-color: #ffffff; | |
font-size: 16px; | |
color: #2d3748; | |
transition: border 0.3s ease; | |
} | |
input:focus, textarea:focus { | |
border-color: #3182ce; | |
outline: none; | |
} | |
.review-btn { | |
background: linear-gradient(45deg, #3182ce, #63b3ed); | |
padding: 14px 28px; | |
border-radius: 12px; | |
font-size: 16px; | |
color: #fff; | |
font-weight: 600; | |
border: none; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.review-btn:hover { | |
transform: scale(1.04); | |
background: linear-gradient(45deg, #63b3ed, #3182ce); | |
} | |
.markdown-container { | |
background: #ffffff; | |
padding: 25px; | |
border-radius: 16px; | |
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); | |
margin-top: 25px; | |
font-size: 15px; | |
line-height: 1.6; | |
} | |
.download-btn { | |
background-color: #38a169; | |
color: white; | |
padding: 12px 20px; | |
border-radius: 10px; | |
font-weight: 600; | |
transition: background 0.3s ease; | |
} | |
.download-btn:hover { | |
background-color: #2f855a; | |
} | |
""" | |
# ------------------ UI ------------------ # | |
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: | |
with gr.Row(): | |
gr.HTML(""" | |
<div style="text-align: center; margin-bottom: 10px;"> | |
<h1 style="color:#2b6cb0; font-size: 2.5rem;">β¨ AI Blog Reviewer</h1> | |
<p style="font-size: 1.1rem; color: #4a5568;"> | |
Analyze your blog for grammar, legal violations, abusive content, and more. | |
</p> | |
</div> | |
""") | |
with gr.Row(equal_height=True): | |
with gr.Column(scale=3): | |
with gr.Tabs(): | |
with gr.TabItem("βοΈ Text Input"): | |
text_input = gr.Textbox( | |
label="Paste Your Blog Content", | |
placeholder="Write or paste your blog text here...", | |
lines=12, | |
elem_classes=["input-text"] | |
) | |
with gr.TabItem("π URL Input"): | |
url_input = gr.Textbox( | |
label="Enter Blog URL", | |
placeholder="https://example.com/blog", | |
lines=1, | |
elem_classes=["input-url"] | |
) | |
with gr.Column(scale=2): | |
gr.HTML(""" | |
<div style="background: #fff; padding: 20px 25px; border-radius: 15px; box-shadow: 0 4px 14px rgba(0,0,0,0.08);"> | |
<h3 style="color:#2b6cb0;">π How It Works</h3> | |
<ul style="font-size: 15px; color: #2d3748; line-height: 1.7;"> | |
<li>Paste blog text or a URL</li> | |
<li>Gemini analyzes for quality and issues</li> | |
<li>Download the review as Markdown</li> | |
</ul> | |
</div> | |
""") | |
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() | |